Home / StructureMap – Configuring StructureMap / StructureMap – The Registry DSL

StructureMap – The Registry DSL

The Registry DSL is the recommended way to configure StructureMap, and 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.  With the advent of .Net 3.5, the Registry DSL is now making use of Expressions as well.

Next Prev

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 different instances of an IRepository interface:

public class RepositoryRegistry : Registry
{
public RepositoryRegistry()
{
// First I'll specify the "default" Instance of IRepository
ForRequestedType<IRepository>().TheDefaultIsConcreteType<InMemoryRepository>();
// Now, I'll add three more Instances of IRepository
ForRequestedType<IRepository>().AddInstances(x =>
{
// "NorthAmerica" is the concrete type DatabaseRepository with
// the connectionString pointed to the NorthAmerica database
x.OfConcreteType<DatabaseRepository>().WithName("NorthAmerica")
.WithCtorArg("connectionString").EqualTo("database=NorthAmerica");
// "Asia/Pacific" is the concrete type DatabaseRepository with
// the connectionString pointed to the AsiaPacific database
x.OfConcreteType<DatabaseRepository>().WithName("Asia/Pacific")
.WithCtorArg("connectionString").EqualTo("database=AsiaPacific");
// Lastly, the "Weird" instance is built by calling a specified
// Lambda (an anonymous delegate will work as well).
x.ConstructedBy(() => WeirdLegacyRepository.Current).WithName("Weird");
});
}
}

 

Applying Registry Classes

The next question is “how does my new Registry class get used?”  When you set up a Container or ObjectFactory, you need to simply direct the Container to use the configuration in that Registry class.

// Example #1
var container1 = new Container(new RepositoryRegistry());
// Example #2
var container2 = new Container(x =>
{
x.AddRegistry<RepositoryRegistry>();
});
// Example #3
ObjectFactory.Initialize(x =>
{
x.AddRegistry<RepositoryRegistry>();
});

 

The Registry DSL Methods

The Registry class exposes these methods for configuration.  Most of these methods are the root of a Fluent Interface expression.

// Registering Types -- BuildInstancesOf() and ForRequestedType() are Synonyms
CreatePluginFamilyExpression<PLUGINTYPE> BuildInstancesOf<PLUGINTYPE>();
CreatePluginFamilyExpression<PLUGINTYPE> ForRequestedType<PLUGINTYPE>();
GenericFamilyExpression ForRequestedType(Type pluginType);
// Shortcut for configuring the default configuration of a concrete type
Registry.BuildWithExpression<T> ForConcreteType<T>();
// Adding additional Instances of a PluginType
IsExpression<T> InstanceOf<T>();
GenericIsExpression InstanceOf(Type pluginType);
// Creating or Adding to a Profile
ProfileExpression CreateProfile(string profileName);
void CreateProfile(string profileName, Action<ProfileExpression> action);
// Interception
void RegisterInterceptor(TypeInterceptor interceptor);
MatchedTypeInterceptor IfTypeMatches(Predicate<Type> match);
// Type Scanning and Auto Registration
void Scan(Action<IAssemblyScanner> action);
// Controlling Setter Injection Behavior
CreatePluginFamilyExpression<PLUGINTYPE> FillAllPropertiesOfType<PLUGINTYPE>();
void SetAllProperties(Action<SetterConvention> action);

Registering Types

The most time consuming part of using an IoC tool like StructureMap is creating the configuration by specifying what types are to be created and how.  Another potentially time consuming activity is trouble shooting that configuration for mistakes and/or trying to understand why the container is behaving the way that it is.  Because of the troubleshooting burden, StructureMap moved away from Xml configuration and towards the programmatic Registry DSL to cut down on errors by using “compiler safe” code for as much of the configuration as possible.  StructureMap departs from other .Net IoC tools by putting a strong emphasis on a readable DSL for the configuration — or at least that was the goal.

Specifying the Default Instance for a PluginType

The most common usage of the Registry DSL is just specifying the default Instance for a given PluginType.  If the concrete type does not have any primitive parameters (i.e., StructureMap can build it completely by using AutoWiring), you can use this shortcut:

ForRequestedType<FluentNHibernate.PersistenceModel>()
.TheDefaultIsConcreteType<DovetailPersistenceModel>();

The method above will throw an exception if the concrete class specified in TheDefaultIsConcreteType<T>() has any primitive arguments (strings, numbers, booleans) in its constructor function.

In other cases, you might need to specify constructor arguments or build an object in other ways than a constructor function.  In that case, you need to use something like this:

// In this case, we need to specify the value of "connectionString" argument to
// the constructor function
x.ForRequestedType<DatabaseRepository>().Use<DatabaseRepository>()
.WithCtorArg("connectionString").EqualToAppSetting("connectionString");

The expression trailing the call to ForRequestedType<T>().TheDefault.Is.************* is an “InstanceExpression.”  The InstanceExpression is reused in several places throughout the Registry DSL to try to enforce a common user experience, reuse code, and enable easier extensions to the Registry DSL.

There is also a limited alternative to the InstanceExpression for times when you cannot use the generic ForRequestedType<T>() method.  For example, with open generic types, you have to use this syntax:

var container = new Container(r =>
{
r.ForRequestedType(typeof (IRepository<>)).TheDefaultIsConcreteType(typeof (OnlineRepository<>));
});

See Using Open Generic Types for more information.

Specifying Additional Instances for a PluginType

The Registry DSL also supports adding additional named instances.  You can add one Instance at a time with the InstanceOf<T>() method, where T is the PluginType.

container = new Container(registry =>
{
registry.InstanceOf<Rule>().Is.OfConcreteType<ARule>().WithName("Alias");
// Add an instance by specifying the ConcreteKey
registry.InstanceOf<IWidget>()
.Is.OfConcreteType<ColorWidget>()
.WithName("Purple")
.WithProperty("color").EqualTo("Purple");
// Specify a new Instance, override a dependency with a named instance
registry.InstanceOf<Rule>().Is.OfConcreteType<WidgetRule>().WithName("RuleThatUsesMyInstance")
.CtorDependency<IWidget>("widget").Is(x => x.TheInstanceNamed("Purple"));
});

 

The InstanceOf<T>().Is.******* method returns an InstanceExpression, so all possible types of Instances are available in this syntax.

If you cannot use the generic InstanceOf<T>() method, you can instead use InstanceOf(Type):

IContainer manager = new Container(r =>
{
r.InstanceOf(typeof (IService<>)).Is(typeof (Service<>)).WithName("Service1");
r.InstanceOf(typeof (IService<>)).Is(typeof (Service2<>)).WithName("Service2");
});

Finally, if you need to add multiple Instances of a PluginType at once, you can use this syntax as a shortcut:

IContainer container = new Container(r => r.ForRequestedType<IAddTypes>().AddInstances(x =>
{
x.OfConcreteType<RedAddTypes>().WithName("Red");
x.OfConcreteType<GreenAddTypes>().WithName("Green");
x.OfConcreteType<BlueAddTypes>().WithName("Blue");
x.OfConcreteType<PurpleAddTypes>();
}));

Each line in the Nested Closure inside AddInstances() represents a completely new Instance for that PluginType.  In this case, the Nested Closure acts on the InstanceExpression.

The Instance “WithName” is not mandatory.  If the name is not specified, StructureMap uses a Guid string to identify the Instance.  Do specify the Name if you want to retrieve the object by Name or for more contextual information in the diagnostics.

Setting the Scoping/Lifecycle for a PluginType

The Scope/Lifecycle of a PluginType can be set in the Registry DSL from either the ForRequestedType<T>() or BuildInstancesOf<T>() method.  All scoping options are available from the CacheBy() method:

var container = new Container(x =>
{
// Scope IGateway as "ThreadLocal"
x.ForRequestedType<IGateway>().CacheBy(InstanceScope.ThreadLocal);
// Scope IService as "HttpSession"
x.BuildInstancesOf<IService>().CacheBy(InstanceScope.HttpSession);
});

There is also a shortcut method for specifying a PluginType as singletons:

// AsSingletons() is just a convenience method that means .CacheBy(InstanceScope.Singleton)
ForRequestedType<IEntityQueryRegistry>().TheDefaultIsConcreteType<EntityQueryRegistry>()
.AsSingletons();

See Scoping and Lifecycle Management for more information.

Registering a “Missing Instance”

In some cases you may be able to intelligently build an object just by the name of the object.  StructureMap 2.5.2+ introduces a new feature called “Missing Instance” that basically equates to “if the Container doesn’t recognize the name in a call to Container.GetInstance<T>(name), use this specified Instance to build the requested object instead of blowing up.”  If the “Missing Instance” isn’t specified, an unknown instance “name” passed into Container.GetInstance<T>(name) will still throw an exception.

Here’s a simple example from the unit tests:

[Test] public void configure_and_use_missing_instance() { // If a user should happen to ask for a Rule by name // that does not exist, StructureMap will use an Instance // that builds a "ColorRule" object using the // IContext.RequestedName property 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"); }

Configuring Concrete Types

Sometimes you may just want to work directly with a concrete type that needs some sort of configuration or dependencies injected, but you don’t need a separate interface contract.  Let’s say that we’re using this concrete class:

public class DatabaseRepository : IRepository
{
private readonly string _connectionString;
public DatabaseRepository(string connectionString)
{
_connectionString = connectionString;
}
}

All we want to do is request an instance of DatabaseRepository and let StructureMap find the connection string from configuration, then use that to build an instance of the DatabaseRepository class.  We could register this class like this:

ObjectFactory.Initialize(x =>
{
x.ForRequestedType<DatabaseRepository>().Use<DatabaseRepository>()
.WithCtorArg("connectionString").EqualToAppSetting("connectionString");
});

With StructureMap 2.5+ we can make a minor shortcut and express the same registration with the ForConcreteType<T>().Configure expression:

ObjectFactory.Initialize(x =>
{
x.ForConcreteType<DatabaseRepository>().Configure
.WithCtorArg("connectionString").EqualToAppSetting("connectionString");
});
// Now, we can request an instance of DatabaseRepository, and
// StructureMap knows to create a new object using the
// connection string from the AppSettings section from the App.config
// file
var repository = ObjectFactory.GetInstance<DatabaseRepository>();

This isn’t that much of a savings in characters typed, but does seem to help with legibility of the code.

Working with Open Generic Types

StructureMap supports open generic types.  See Using Open Generic Types for more information about registering open generic types.

Auto Registration and Assembly ScanningScanning

The Registry DSL exposes the Scan() method to specify auto registration policies.  See Auto Registration with Type Scanning for more information.

Setter Injection Policies

The Registry DSL exposes the FillAllPropertiesOfType() method and the SetAllProperties() methods to create policies for which setter properties of concrete types will be mandatory properties that will always be injected by StructureMap.  See Creating Policies for Setter Injection for more information.

Interception

The Registry DSL exposes a couple methods for configuring interception policies. See Interception for more information.

Creating a Profile

The Registry DSL exposes methods for defining or adding to Profiles.  See Profiles for more information.

Using the Registry DSL Directly in Initializationlization

The Container.Initialize() and Container.Configure() methods both support all of the Registry DSL methods. It is not recommended that you use this functionality in larger systems, but it can be very handy for small systems or one off development activities.

ObjectFactory.Initialize(x => { x.ForRequestedType<IRepository>().TheDefaultIsConcreteType<InMemoryRepository>(); x.ForRequestedType<IRepository>().AddInstances(y => { y.OfConcreteType<DatabaseRepository>().WithName("NorthAmerica") .WithCtorArg("connectionString").EqualTo("database=NorthAmerica"); y.OfConcreteType<DatabaseRepository>().WithName("Asia/Pacific") .WithCtorArg("connectionString").EqualTo("database=AsiaPacific"); y.ConstructedBy(() => WeirdLegacyRepository.Current).WithName("Weird"); }); });

 

Next Prev