Fork me on GitHub

Working with the IContext at Build Time Edit on GitHub


The IContext usage shown in this topic is certainly not going away in future versions of StructureMap, but if at all possible, the StructureMap team strongly recommends using Construction Policies to accomplish customized object building instead of relying on conditional logic using IContext at runtime.

The IContext interface is what StructureMap uses to expose the current state of the internal "build session" for the current object creation operation for usage in user-supplied lambda builders or interceptors. IContext allows you to:

  1. Retrieve other services from the current Container to use in a Lambda
  2. "BuildUp" objects with setter dependencies
  3. Retrieve all the objects created inside the current build session that could be cast to a certain interface or base class
  4. If an object is being built as a dependency to another object, see the parent type
  5. See the root type of the top level object that StructureMap is building in this request

The entire IContext interface is shown below:


public interface IContext
{
    string RequestedName { get; }

    void BuildUp(object target);

    T GetInstance<T>();

    T GetInstance<T>(string name);

    object GetInstance(Type pluginType);

    object GetInstance(Type pluginType, string instanceKey);

    T TryGetInstance<T>() where T : class;

    T TryGetInstance<T>(string name) where T : class;

    object TryGetInstance(Type pluginType);

    object TryGetInstance(Type pluginType, string instanceKey);

    IEnumerable<T> All<T>() where T : class;


    IEnumerable<T> GetAllInstances<T>();

    IEnumerable<object> GetAllInstances(Type pluginType);

    Type ParentType { get; }

    Type RootType { get; }
}

Example 1: Contextual Logging

All of the sample code in this example is in Github.

One of the canonical examples of using IContext is the integration of logging frameworks like NLog or Log4net that allow you to create logging policies by concrete type. With these tools, you generally have a static class (boo!) where you ask for the proper logging service for a type of object like this one:


public static class LoggerFactory
{
    public static Logger LoggerFor(Type type)
    {
        return new Logger(type);
    }
}

Now, say you would want to have the proper Logger injected into every object that StructureMap creates that depends on Logger matching the concrete type of the object being created. That registration is shown below in a unit test from StructureMap's codebase:


[Fact]
public void can_happily_use_the_parent_type()
{
    var container = new Container(x =>
    {
        // AlwaysUnique() is important so that every object created will get
        // their own Logger instead of sharing whichever one is created first
        x.For<Logger>().Use(c => LoggerFactory.LoggerFor(c.ParentType)).AlwaysUnique();
    });

    var top = container.GetInstance<LoggedClass1>();
    top.Logger.ParentType.ShouldBe(typeof(LoggedClass1));
    top.Child.Logger.ParentType.ShouldBe(typeof(LoggedClass2));
    top.Child.Child.Logger.ParentType.ShouldBe(typeof(LoggedClass3));
}

Just for fun, here's the equivalent with the new construction policies from 4.0:


public class LoggerConvention : ConfiguredInstancePolicy
{
    protected override void apply(Type pluginType, IConfiguredInstance instance)
    {
        instance.Constructor
            .GetParameters()
            .Where(param => param.ParameterType == typeof(Logger))
            .Each(param => instance.Dependencies.Add(LoggerFactory.LoggerFor(instance.PluggedType)));
    }
}

[Fact]
public void use_logger_convention()
{
    var container = new Container(_ =>
    {
        _.Policies.Add<LoggerConvention>();
    });

    var top = container.GetInstance<LoggedClass1>();
    top.Logger.ParentType.ShouldBe(typeof(LoggedClass1));
    top.Child.Logger.ParentType.ShouldBe(typeof(LoggedClass2));
    top.Child.Child.Logger.ParentType.ShouldBe(typeof(LoggedClass3));
}

The LoggerConvention way of accomplishing the logging integration is technically more code and possibly harder to understand, but it's significantly more efficient at runtime because the decision about which Logger to use is only done once upfront. The conventional approach should also be more evident in StructureMap diagnostics.

Example 2: NHibernate Integration

It's been several years since I used NHibernate, so I might not have the technical details exactly right here:)

If you're developing a system with NHibernate managed by StructureMap, you will frequently need to inject NHibernate's ISession service into your concrete classes. There's a little catch though, to create an ISession object you have to use NHibernate's ISessionFactory service:


public interface ISession { }

public interface ISessionFactory
{
    ISession Build();
}

Sidestepping the issue of how to build an ISessionFactory, here's a possible way to enable a StructureMap Container to build and resolve dependencies of ISession with a lambda builder:


public class SessionFactoryRegistry : Registry
{
    // Let's not worry about how ISessionFactory is built
    // in this example
    public SessionFactoryRegistry(ISessionFactory factory)
    {
        For<ISessionFactory>().Use(factory);

        // Build ISession with a lambda:
        For<ISession>().Use("Build ISession from ISessionFactory", c =>
        {
            // To resolve ISession, I first pull out
            // ISessionFactory from the IContext and use that
            // to build a new ISession. 
            return c.GetInstance<ISessionFactory>().Build();
        });
    }
}