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.

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in StructureMap. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • http://realfiction.net Frank Quednau

    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? ;)