Creating Registry
classes is the recommended way of using the Registry DSL.
The Registry DSL is mostly a fluent interface with some nested closure usage. The intent of the Registry DSL is to make the configuration process as error free as possible by using "compiler safe" expressions and defensive programming to point out missing data.
The Registry Class
On all but the smallest systems, the main unit of configuration will probably be
the Registry
class. Typically, you would subclass the Registry
class, then
use the fluent interface methods exposed by the Registry class to create Container
configuration. Here's a sample Registry
class below used to configure an
instance of an IWidget
interface:
public class PurpleRegistry : Registry
{
public PurpleRegistry()
{
For<IWidget>().Use<AWidget>();
}
}
Including Registries
The next question is "how does my new Registry
class get used?"
When you set up a Container
, you need to simply direct the
Container
to use the configuration in that Registry
class:
[Fact]
public void include_a_registry()
{
var registry = new Registry();
registry.IncludeRegistry<YellowBlueRegistry>();
registry.IncludeRegistry<RedGreenRegistry>();
registry.IncludeRegistry<PurpleRegistry>();
// build a container
var container = new Container(registry);
// verify the default implementation and total registered implementations
container.GetInstance<IWidget>().ShouldBeOfType<AWidget>();
container.GetAllInstances<IWidget>().Count().ShouldBe(5);
}
Use versus Add
Registrations in the Registry DSL can be done with either Add()
or Use()
methods, but they have
a different semantic meaning to StructureMap. Add()
means add another Instance to this plugin type
while Use()
means this one is the default.
One of the things that is different about StructureMap is that if it has multiple registrations of any
given plugin type, one of these registrations has to be explicitly marked as the default usage for that plugin type
or StructureMap will blow up in the call to Container.GetInstance()
. Other IoC tools will magically use
the first registration or the last registration (and some even allow you to configure that behavior). We chose to
make that determination be explicit.
As of StructureMap 3.0, the WhatDoIHave() output is part of any exception thrown by StructureMap when it cannot determine a default registration for a requested type if there is more than one registration for that type.
If there are multiple calls to Use()
for the same plugin type, the last one wins. For more control over this behavior in
modularity scenarios, see Fallback Services and Replace or Clear Out Previous Registrations.
For().Use()
To register the default Instance
of a type, the syntax is one of the Registry.For().Use()
overloads shown below:
public class SettingDefaults : Registry
{
public SettingDefaults()
{
// If you know the plugin type and its a closed type
// you can use this syntax
For<IWidget>().Use<DefaultWidget>();
// By Lambda
For<IWidget>().Use(() => new DefaultWidget());
// Pre-existing object
For<IWidget>().Use(new AWidget());
// This is rare now, but still valid
For<IWidget>().Add<AWidget>().Named("A");
For<IWidget>().Add<BWidget>().Named("B");
For<IWidget>().Use("A"); // makes AWidget the default
// Also rare, but you can supply an Instance object
// yourself for special needs
For<IWidget>().UseInstance(new MySpecialInstance());
// If you're registering an open generic type
// or you just have Type objects, use this syntax
For(typeof (IService<>)).Use(typeof (Service<>));
// This is occasionally useful for generic types
For(typeof (IService<>)).Use(new MySpecialInstance());
}
}
For().Add()
To register additional Instances
for a plugin type, use one of the overloads of For().Add()
:
public class AdditionalRegistrations : Registry
{
public AdditionalRegistrations()
{
// If you know the plugin type and its a closed type
// you can use this syntax
For<IWidget>().Add<DefaultWidget>();
// By Lambda
For<IWidget>().Add(() => new DefaultWidget());
// Pre-existing object
For<IWidget>().Add(new AWidget());
// Also rare, but you can supply an Instance object
// yourself for special needs
For<IWidget>().AddInstance(new MySpecialInstance());
// If you're registering an open generic type
// or you just have Type objects, use this syntax
For(typeof(IService<>)).Add(typeof(Service<>));
// This is occasionally useful for generic types
For(typeof(IService<>)).Add(new MySpecialInstance());
}
}
Add Many Registrations with For().AddInstances()
If you need to add several Instances
to a single plugin type, the AddInstances()
syntax
shown below may be quicker and easier to use:
// registry is a StructureMap Registry object
registry.For<IService>().AddInstances(x =>
{
// Equivalent to For<IService>().Add<ColorService>().....
x.Type<ColorService>().Named("Red").Ctor<string>("color").Is("Red");
// Equivalent to For<IService>().Add(new ColorService("Yellow"))......
x.Object(new ColorService("Yellow")).Named("Yellow");
// Equivalent to For<IService>().Use(() => new ColorService("Purple"))....
x.ConstructedBy(() => new ColorService("Purple")).Named("Purple");
x.Type<ColorService>().Named("Decorated").Ctor<string>("color").Is("Orange");
});
Named Instances
When you have multiple implementations of an interface, it can often be useful to name instances. To retrieve a specific implementation:
[Fact]
public void SimpleCaseWithNamedInstance()
{
container = new Container(x => { x.For<IWidget>().Add<AWidget>().Named("MyInstance"); });
// retrieve an instance by name
var widget = (AWidget)container.GetInstance<IWidget>("MyInstance");
widget.ShouldNotBeNull();
}
You can also register named instances with the following shorthand:
[Fact]
public void A_concrete_type_is_available_by_name_when_it_is_added_by_the_shorthand_mechanism()
{
IContainer container = new Container(r => r.For<IAddTypes>().AddInstances(x =>
{
x.Type<RedAddTypes>().Named("Red");
x.Type<GreenAddTypes>().Named("Green");
x.Type<BlueAddTypes>().Named("Blue");
x.Type<PurpleAddTypes>();
}));
// retrieve the instances by name
container.GetInstance<IAddTypes>("Red").IsType<RedAddTypes>();
container.GetInstance<IAddTypes>("Green").IsType<GreenAddTypes>();
container.GetInstance<IAddTypes>("Blue").IsType<BlueAddTypes>();
}