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:
- Return the concrete type "ARule" if the condition "c => false" is met (in real
usage the predicate would do something more intelligent ;-) )
- Else, return a Rule object specified by the variable named GREEN if the
condition "c => true" is met
- 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"):
- Internally, the Container object finds the ConditionalInstance object that was
configured and named "conditional" for the PluginType "Rule"
- The Container invokes the ConditionalInstance.Build(Type, BuildSession) method
- The ConditionalInstance evaluates its InstanceCase collection to find the first
InstanceCase that matches the current IContext and invokes the internal Instance
of that InstanceCase
- 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.
Posted
Sun, Jan 18 2009 11:19 PM
by
Jeremy D. Miller