While you generally allow StructureMap to just use auto-wiring to fill the dependencies of a concrete type, there are times when you may want to explicitly configure individual dependencies on a case by case basis. In the case of primitive types like strings or numbers, StructureMap will not do any auto-wiring, so it's incumbent upon you the user to supply the dependency.
Let's say we have a simple class called ColorWidget
like the following:
public class ColorWidget : IWidget
{
public string Color { get; set; }
public ColorWidget(string color)
{
Color = color;
}
}
To register the ColorWidget
, you would supply the value of the color
parameter to the constructor function like so:
[Fact]
public void inline_usage_of_primitive_constructor_argument()
{
var container = new Container(_ =>
{
_.For<IWidget>().Use<ColorWidget>()
.Ctor<string>().Is("Red");
});
container.GetInstance<IWidget>()
.ShouldBeOfType<ColorWidget>()
.Color.ShouldBe("Red");
}
Event Condition Action Rules
The ability to explicitly define dependencies inline isn't commonly used these days, but was actually one of the very core use cases in the initial versions of StructureMap. One of the first usages of StructureMap in a production application was in a configurable rules engine using an Event-Condition-Action architecture where the conditions and actions were configured in StructureMap as inline dependencies of Rule objects. Using StructureMap's old Xml configuration, we could define rules for new customers by registering rule objects with the container that reused existing condition and action classes in new configurations.
To make that concrete and establish a sample problem domain, consider these types:
public class SomeEvent
{
}
public interface ICondition
{
bool Matches(SomeEvent @event);
}
public interface IAction
{
void PerformWork(SomeEvent @event);
}
public interface IEventRule
{
void ProcessEvent(SomeEvent @event);
}
Now, let's move on to seeing how we could use inline dependency configuration to register new rules.
Constructor Parameters by Type
First off, let's say that we have a SimpleRule
that takes a single condition and action:
public class SimpleRule : IEventRule
{
private readonly ICondition _condition;
private readonly IAction _action;
public SimpleRule(ICondition condition, IAction action)
{
_condition = condition;
_action = action;
}
public void ProcessEvent(SomeEvent @event)
{
if (_condition.Matches(@event))
{
_action.PerformWork(@event);
}
}
}
Now, since SimpleRule
has only a single dependency on both IAction
and ICondition
, we can create new rules by registering new Instance's
of SimpleRule
with different combinations of its dependencies:
public class InlineCtorArgs : Registry
{
public InlineCtorArgs()
{
// Defining args by type
For<IEventRule>().Use<SimpleRule>()
.Ctor<ICondition>().Is<Condition1>()
.Ctor<IAction>().Is<Action1>()
.Named("One");
// Pass the explicit values for dependencies
For<IEventRule>().Use<SimpleRule>()
.Ctor<ICondition>().Is(new Condition2())
.Ctor<IAction>().Is(new Action2())
.Named("Two");
// Use Lambda construction
For<IEventRule>().Use<SimpleRule>()
.Ctor<ICondition>().Is(() => new Condition3())
.Ctor<IAction>().Is("some crazy builder", c => c.GetInstance<Action3>())
.Named("Three");
// Rarely used, but gives you a "do any crazy thing" option
// Pass in your own Instance object
For<IEventRule>().Use<SimpleRule>()
.Ctor<IAction>().Is(new MySpecialActionInstance());
// Inline configuration of your dependency's dependencies
For<IEventRule>().Use<SimpleRule>()
.Ctor<ICondition>().IsSpecial(_ => _.Type<BigCondition>().Ctor<int>().Is(100))
// or
.Ctor<ICondition>().Is(new SmartInstance<BigCondition>().Ctor<int>().Is(100));
}
public class BigCondition : ICondition
{
public BigCondition(int number)
{
}
public bool Matches(SomeEvent @event)
{
throw new NotImplementedException();
}
}
public class MySpecialActionInstance : LambdaInstance<Action3>
{
public MySpecialActionInstance()
: base(() => new Action3())
{
}
}
}
The inline dependency configuration using the Ctor<T>().Is()
syntax supports all the common StructureMap configuration options: define by type, by lambdas, by value, or if you really want to risk severe eye strain, you can use your own Instance objects and define the configuration of your dependency's dependencies.
Specifying the Argument Name
If for some reason you need to specify an inline constructor argument dependency, and the concrete type has more than one dependency for that type, you just need to specify the parameter name as shown in this sample:
public class DualConditionRule : IEventRule
{
private readonly ICondition _first;
private readonly ICondition _second;
private readonly IAction _action;
public DualConditionRule(ICondition first, ICondition second, IAction action)
{
_first = first;
_second = second;
_action = action;
}
public void ProcessEvent(SomeEvent @event)
{
if (_first.Matches(@event) || _second.Matches(@event))
{
_action.PerformWork(@event);
}
}
}
public class DualConditionRuleRegistry : Registry
{
public DualConditionRuleRegistry()
{
// In this case, because DualConditionRule
// has two different
For<IEventRule>().Use<DualConditionRule>()
.Ctor<ICondition>("first").Is<Condition1>()
.Ctor<ICondition>("second").Is<Condition2>();
}
}
Setter Dependencies
You can also configure setter dependencies with a similar syntax, but with additional options to specify the property name
by using an Expression
as shown below:
public class RuleWithSetters : IEventRule
{
public ICondition Condition { get; set; }
public IAction Action { get; set; }
public void ProcessEvent(SomeEvent @event)
{
if (Condition.Matches(@event))
{
Action.PerformWork(@event);
}
}
}
public class RuleWithSettersRegistry : Registry
{
public RuleWithSettersRegistry()
{
For<IEventRule>().Use<RuleWithSetters>()
.Setter<ICondition>().Is<Condition1>()
// or
.Setter(x => x.Action).Is(new Action1())
// or if you need to specify the name
.Setter<IAction>("Action").Is<Action2>()
// or you can configure values *after* the object
// is constructed with the SetProperty method
.SetProperty(x => x.Action = new Action2());
}
}
Enumerable Dependencies
TODO(show a sample of using enumerable dependencies)
Programmatic Configuration outside of the Registry DSL
In some cases, you may want to skip the Registry DSL and go straight for the raw dependencies structures. Let's say that we're using an open generic type for our rules engine so that we can respond to multiple event types:
public interface IEventRule<TEvent>
{
void ProcessEvent(TEvent @event);
}
public interface ICondition<TEvent>
{
bool Matches(TEvent @event);
}
public class Condition1<TEvent> : ICondition<TEvent>
{
public bool Matches(TEvent @event)
{
throw new NotImplementedException();
}
}
public interface IAction<TEvent>
{
void PerformWork(TEvent @event);
}
public class Action1<TEvent> : IAction<TEvent>
{
public void PerformWork(TEvent @event)
{
throw new NotImplementedException();
}
}
public class EventRule<TEvent> : IEventRule<TEvent>
{
private readonly string _name;
private readonly ICondition<TEvent> _condition;
private readonly IAction<TEvent> _action;
public EventRule(string name, ICondition<TEvent> condition, IAction<TEvent> action)
{
_name = name;
_condition = condition;
_action = action;
}
public string Name
{
get { return _name; }
}
public void ProcessEvent(TEvent @event)
{
if (_condition.Matches(@event))
{
_action.PerformWork(@event);
}
}
}
As an alternative approach, we could build up ConstructorInstance
objects to represent our rules like so:
public class OpenTypesRegistry : Registry
{
public OpenTypesRegistry()
{
var instance = new ConstructorInstance(typeof(EventRule<>));
// By name
instance.Dependencies.Add("action", typeof(Action1<>));
// Everything else is syntactical sugur over this:
instance.Dependencies.Add(new Argument
{
Type = typeof(IAction<>), // The dependency type
Name = "action", // The name of the dependency, either
// a constructor argument name or
// the name of a setter property
// Specify the actual dependency
// This can be either a concrete type, the prebuilt value,
// or an Instance
Dependency = typeof(Action1<>)
});
}
}
It's frequently useful to explicitly configure all the elements for an enumerable argument (arrays, IEnumerable, or IList). StructureMap provides this syntax to do just that:
public class BigRule : IEventRule
{
private readonly IEnumerable<ICondition> _conditions;
private readonly IEnumerable<IAction> _actions;
public BigRule(IEnumerable<ICondition> conditions, IEnumerable<IAction> actions)
{
_conditions = conditions;
_actions = actions;
}
public void ProcessEvent(SomeEvent @event)
{
if (_conditions.Any(x => x.Matches(@event)))
{
_actions.Each(x => x.PerformWork(@event));
}
}
}
public class BigRuleRegistry : Registry
{
public BigRuleRegistry()
{
For<IEventRule>().Use<BigRule>()
// Each line in the nested closure adds another
// ICondition to the enumerable dependency in
// the order in which they are configured
.EnumerableOf<ICondition>().Contains(_ =>
{
_.Type<Condition1>();
_.Type<Condition2>();
})
.EnumerableOf<IAction>().Contains(_ =>
{
_.Type<Action1>();
_.Object(new Action2());
});
}
}