Jeremy D. Miller -- The Shade Tree Developer

Sponsors

The Lounge

Wicked Cool Jobs

Syndication

News

Advertisement

Images in this post missing? We recently lost them in a site migration. We're working to restore these as you read this. Should you need an image in an emergency, please contact us at imagehelp@codebetter.com
Using the Build Session in StructureMap

Here's your StructureMap Tip O' the Day.  StructureMap 2.5+ introduces the "Build Session" that is a group of objects that give you access to information about the current object request.  In several spots you can query the BuildSession (IContext) to alter construction or even to grab other services to do your construction.  This is repeated from http://structuremap.sourceforge.net/UsingSessionContext.htm.

 

 

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;

    }


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 tht is responsible for creating the NHibernate ISession objects (it wraps a Sessio).

    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.


Posted Thu, Jan 15 2009 8:59 AM by Jeremy D. Miller
Filed under:

[Advertisement]

Comments

Frank Quednau wrote re: Using the Build Session in StructureMap
on Fri, Jan 16 2009 3:45 AM

Thanks for the additions in BuildSession, although I can see now that even my scenario where the key for the correct implementation is contained as dependency itself could be handled without a special instance.

It is extreme how the lambda syntax manages to keep code that is in fact totally disparate in terms of the passing of time together in a coherent fashion.

Can we call this time-agnostic design? ;)

Add a Comment

(required)  
(optional)
(required)  
Remember Me?
Devlicio.us