Home / StructureMap – Using the Session Context

StructureMap – Using the Session Context

A new aspect of the StructureMap internal architecture is the BuildSession class that tracks the objects being created within a single call to any of the Container.GetInstance() methods.  While it is mostly an internal feature, in some cases users can take advantage of the exposed IContext interface to query information about the current request and to even register or retrieve the dependencies that will be used within that object request.  The IContext interface is shown below:

public interface IContext
{
/// <summary>
/// Gets a reference to the <see cref="BuildStack">BuildStack</see> for this build session
/// </summary>
BuildStack BuildStack { get; }
/// <summary>
/// The concrete type of the immediate parent object in the object graph
/// </summary>
Type ParentType { get; }
/// <summary>
/// Get the object of type T that is valid for this build session.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T GetInstance<T>();
/// <summary>
/// Get the object of type T that is valid for this build session by name.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T GetInstance<T>(string name);
/// <summary>
/// Gets the root "frame" of the object request
/// </summary>
BuildFrame Root { get; }
/// <summary>
/// The requested instance name of the object graph
/// </summary>
string RequestedName { get; }
/// <summary>
/// Register a default object for the given PluginType that will
/// be used throughout the rest of the current object request
/// </summary>
/// <param name="pluginType"></param>
/// <param name="defaultObject"></param>
void RegisterDefault(Type pluginType, object defaultObject);
/// <summary>
/// Same as GetInstance, but can gracefully return null if
/// the Type does not already exist
/// </summary>
/// <typeparam name="T"></typeparam>
/// <returns></returns>
T TryGetInstance<T>() where T : class;
/// <summary>
/// Same as GetInstance(name), but can gracefully return null if
/// the Type and name does not already exist
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="name"></param>
/// <returns></returns>
T TryGetInstance<T>(string name) where T : class;
}
Next Prev

Using the Build Stack

You can interrogate the IContext.BuildStack property to access the PluginType and name of the instance being created at any time.  BuildStack will give you access both to the “root” of the request and the immediate “parent” of the object being created.  The BuildStack keeps track of a BuildFrame (somewhat modeled on the StackFrame in the BCL) at each level of the request:

public interface IBuildFrame { /// <summary> /// The requested PluginType of the Instance being create /// </summary> Type RequestedType { get; } /// <summary> /// The Name of the Instance being created /// </summary> string Name { get; } /// <summary> /// The actual ConcreteType being created. This will not always /// be available /// </summary> Type ConcreteType { get; } }

IContext exposes a property for the full BuildStack, but also exposes some convenience methods for getting at the immediate parent type.  The driver for this property was a request to recreate the Logging Facility from Windsor.  Let’s say that you want some sort of generic setter policy for logging.  Any time StructureMap builds an object and sees a public settable property of your “ILogger” instance, it should use the type of being created to find the properly configured ILogger and then attach that new ILogger to the parent object.

Here’s a sample of code that uses the IContext.ParentType to create the proper ILogger.

var container = new Container(r => { r.FillAllPropertiesOfType<ILogger>().TheDefault.Is .ConstructedBy(context => new Logger(context.ParentType)); });

Using the Requested Name

The IContext interface will allow you to use the instance name that was requested in a call to Container.GetInstance<T>(name) or ObjectFactory.GetNamedInstance<T>(name).  The most common usage is probably going to be within the new “Missing Instance” feature (inspired by the Missing Method feature of dynamic languages like Ruby).   The immediate way that my team is going to use the Missing Instance functionality is to build an object for localization support based on the requested culture.

Here’s an example of using Missing Instance that queries the IContext.RequestedName property in order to construct an object:

[Test]
public void configure_and_use_missing_instance()
{
var container = new Container(x =>
{
x.ForRequestedType<Rule>().MissingNamedInstanceIs.ConstructedBy(context => new ColorRule(context.RequestedName));
});
container.GetInstance<Rule>("red").ShouldBeOfType<ColorRule>().Color.ShouldEqual("red");
container.GetInstance<Rule>("green").ShouldBeOfType<ColorRule>().Color.ShouldEqual("green");
container.GetInstance<Rule>("blue").ShouldBeOfType<ColorRule>().Color.ShouldEqual("blue");
}

Retrieving a Service from IContext

You can also retrieve other services from the IContext during object construction.  Because the underlying BuildSession manages the Auto Wiring, you can generally assume that you’re using the exact same object instance for a PluginType that other objects in the same object graph will receive.  That’s a helpful feature when you’re talking about using View’s within any type of desktop application or any kind of NHibernate object where the state or identity of the object requested is important.

My team uses this functionality in our NHibernate bootstrapping.  We have an interface named ISessionSource that is responsible for creating the NHibernate ISession objects (it wraps a Session).

public interface ISessionSource { ISession CreateSession(); }

We can’t just walk up and create an ISession object directly.  Instead, you have to use the ISessionSource to create an ISession for you.  We still want StructureMap to inject the ISession objects into other classes, so we use the IContext.GetService<ISession>() method from within a Lambda to build ISession objects:

ForRequestedType<ISession>().TheDefault.Is.ConstructedBy( context => context.GetInstance<ISessionSource>().CreateSession());

The IContext.GetInstance<T>() function will also allow you to retrieve objects explicitly passed into the Container through the Container.With<T>(T target).GetInstance() methods.

Registering a Service with IContext

You can alter the underlying BuildSession and control all following Dependency Injection by overriding the default objects within a single object request by using the IContext.RegisterDefault() method.  Use this method with extreme caution.

Next Prev