Jonathan Birkholz

Structure Map 2.6 Using Conventions

An awesome feature of StructureMap is the ability to scan assemblies and register types using different conventions. At first this might seem a trivial feature but I had a project in the past where this approach was shunned upon. Even when I tried to bring in this functionality it was not approved. The result? A ‘module’ class with tons of namespaces and tons of register lines. Why suffer that pain?!

Default Convention

How many times do we have an IFoo interface and then the concrete implementation named Foo. Shouldn’t our IoC container be smart enough to recognized this most basic and popular pattern? Well StructureMap is that smart.

To use conventions, we first specify which assemblies we want to scan. Then we add conventions to use when scanning those assemblies. So to use the default convention scanner we simply do:

1
2
3
4
5
6
container.Configure(x => x.Scan(s =>
{
s.TheCallingAssembly();
s.Convention<DefaultConventionScanner>();
} ));

And naturally we should have a test to verify it works as we expect.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[Test]
public void should_load_IFoo()
{
var container = new Container();
container.Configure(x => x.Scan(s =>
{
s.TheCallingAssembly();
s.Convention<DefaultConventionScanner>();
} ));
// IFoo -> Foo
// IWhatever -> Whatever
var foo = container.GetInstance<IFoo>();
foo.ShouldBeType<Foo>();
}

Add All Types

Sometimes we have more than one implementation of a given interface. For that scenario we can use the same scanning process but this time tell the scanner to add of types of a given interface.

1
s.AddAllTypesOf<IEntity>()

What’s even cooler is that we can add additional information to the registration. For example, what if we want to register all our IEntity types but use the actual type name as a key (PersonEntity –> ‘Person’). Easy peasy.

s.AddAllTypesOf().NameBy(t => t.Name.Replace(“Entity”, “”)); And our test?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[Test]
public void should_load_Person_Entity()
{
var container = new Container();
container.Configure(x => x.Scan(s =>
{
s.TheCallingAssembly();
s.AddAllTypesOf<IEntity>().NameBy(t => t.Name.Replace("Entity", ""));
}));
var person = container.GetInstance<IEntity>("Person");
person.ShouldBeType<PersonEntity>();
}

Custom Convention

Beyond the built in Default Convention and the ability to add all types, we can create our own custom conventions. These used to be called TypeScanners, but are now implementations of the IRegistrationConvention interface. I think the renaming was a good idea. I always had a hard time naming my scanners because I kept calling them conventions. Now I don’t have that problem anymore. :)

The interface is simple and has only one method you need to implement called Process.

1
2
3
4
5
6
7
public class CustomConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
throw new NotImplementedException();
}
}

For our custom convention we are going to register all types of IController and use the type name as the key. So this is similar to how we registered our IEntity but instead of using the AddAllTypes feature we are using a convention.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class ControllerConvention : IRegistrationConvention
{
public void Process(Type type, Registry registry)
{
if (!type.CanBeCastTo(typeof (IController)))
return;
string name = GetName(type);
registry.AddType(typeof (IController), type, name);
}
static string GetName(Type type)
{
return type.Name.Replace("Controller", "");
}
}

And then to use our new conventions we just need to do:

1
2
3
4
5
6
7
_container = new Container();
_container.Configure(x => x.Scan(s =>
{
s.TheCallingAssembly();
s.Convention<ControllerConvention>();
}));

Here are the tests:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[Test]
public void should_use_register_a_single_controller()
{
var count = _container.GetAllInstances<IController>().Count;
count.ShouldBe(1);
}
[Test]
public void should_register_controller_for_person()
{
var controller = _container.GetInstance<IController>("Person");
controller.ShouldBeType<PersonController>();
}

Here we just covered the basics of using conventions and the assembly scanner with StructureMap to reduce wasteful registration code.

The code can be found in my learning solution on GitHub : http://github.com/RookieOne/Learning