Conditional Object Construction in StructureMap (i.e. Fun with Lambdas)

What if you want StructureMap to return a different Type for users in one role versus user without that role?  What if you’d like to return one Type if the database is reachable and another Type if the database is determined to be offline?  I kept getting questions like that on the StructureMap group, so I applied some Lambda-fu in the latest release to create the new “Conditional Instance.” This blog is excerpted from the StructureMap documentation here.

 

There have been several questions on the StructureMap users list about doing
conditional construction (i.e., return this object if this condition, else this
other object).  In order to meet this apparent need,
StructureMap 2.5.2 introduces the new ConditionalInstance that allows a user to
effectively switch the active Instance based on a Predicate<IContext> boolean
test.  Here’s a quick example of using the new Conditional() syntax of
InstanceExpression:

           
var
container = new
Container(x =>

            {

               
x.InstanceOf<Rule>().Is.Conditional(o =>

                {

                   
o.If(c => false).ThenIt.Is.OfConcreteType<ARule>();

                   
o.If(c => true).ThenIt.IsThis(GREEN);

                   
o.TheDefault.IsThis(RED);

               
}).WithName(“conditional”);

            });

The syntax above is configuring and attaching a ConditionalInstance object. 
Internally, this syntax is telling the ConditionalInstance to:

  1. Return the concrete type “ARule” if the condition “c => false” is met (in real
    usage the predicate would do something more intelligent ;-) )
  2. Else, return a Rule object specified by the variable named GREEN if the
    condition “c => true” is met
  3. Finally, if none of the predicates match, return the Rule object specified by
    the variable named RED

The syntax “If( predicate ).*************” uses an InstanceExpression and all
possible Instance types are available.

Internally, the ConditionalInstance looks like this:

    public class
ConditionalInstance<T> : ExpressedInstance<ConditionalInstance<T>>

    {

        // Conditional Instance
keeps track of zero or more internal Instance

        // objects against a
Predicate<IContext> condition

        private
readonly List<InstanceCase> _cases =
new List<InstanceCase>();

 

        // The “default” Instance
to use if none of the conditional predicates

        // are met.  If this
is not explicitly defined, the ConditionalInstance

        // will simply look for
the default Instance of the desired

        // PluginType

        public
Instance _default =
new
DefaultInstance
();

    }

 

    public class
InstanceCase

    {

        public
Predicate<IContext>
Predicate { get;
set
; }

        public
Instance Instance {
get; set; }

    }

When a call is made to
container.GetInstance<Rule>(“conditional”):

  1. Internally, the Container object finds the ConditionalInstance object that was
    configured and named “conditional” for the PluginType “Rule”
  2. The Container invokes the ConditionalInstance.Build(Type, BuildSession) method
  3. The ConditionalInstance evaluates its InstanceCase collection to find the first
    InstanceCase that matches the current IContext and invokes the internal Instance
    of that InstanceCase
  4. Lastly, if ConditionalInstance does not find any matching InstanceCase objects,
    it will invoke its default Instance to build the requested object

It might be easier to just see the code for this:

        protected
override object
build(Type pluginType,
BuildSession session)

        {

           
// Find the first InstanceCase that matches the BuildSession/IContext

           
var
instanceCase = _cases.Find(c => c.Predicate(session));

 

           
// Use the Instance from the InstanceCase if it exists,

           
// otherwise, use the “default”

           
var
instance = instanceCase == null ?
_default : instanceCase.Instance;

 

           
// delegate to the chosen Instance

           
return
instance.Build(pluginType, session);

        }

Please see Using the Session Context for
more information on what is possible with the IContext.

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://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Daniel,

    I did this feature mostly because it sounded like a cool thing to see if I could do it. Whether it’s widely used or not, shrug. We’ll see.

  • http://www.blogcoward.com jdn

    That’s sweet.

  • http://blog@danielfernandes.net Daniel Fernandes

    @Jeremy:
    Your choice of wording makes it as if you’ve implemented this feature against your will ?

    I personally think what you’ve done is great because it should be possible to put brokers functionality at any stage of a graph retrieval.

    The problem I had with the normal use case of IoC is that to differentiate two instances that do essentially the same thing by implementing the same contract but are implemented in different ways.
    It’s usually either by convention (parameter name) or using the typing to provide unique distinction which is all well and good but it always felt to me an over-simplification of object construction.

    So in my view what you’ve done is giving an easy API to provide a hook at the core of the IoC container and can truly enable more dynamic scenarios without ending up with a type soup.
    So for this I say thank you!

    Daniel

  • Chris

    hi

    wow – that looks ace. Ive been trying to find out if something like this was possible with an IoC container

    I asked on Stackoverflow but didnt get any satisfactory answers

    http://stackoverflow.com/questions/282406/does-an-ioc-container-replace-the-use-of-factories

    would you have any comment on the suitability of what you describe here to addressing the situation in the linked question

    it would be much appreciated – I find myself writing code like that alot and to have it automated would be great