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:
- Retrieve other services from the current
Container
to use in a Lambda - "BuildUp" objects with setter dependencies
- Retrieve all the objects created inside the current build session that could be cast to a certain interface or base class
- If an object is being built as a dependency to another object, see the parent type
- 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();
});
}
}