21
Aug
09

StructureMap and Scanning With Custom Conventions

On my new project we are attempting to use convention over configuration wherever possible to make the developer’s lives easier and our code less error prone.

One way we are doing this is by creating custom type scanners in StructureMap (our selected IoC container).

For example, we haveĀ  an interface IEntityMapper<T> that defines the base contract for mapping POCO classes into SharePoint list items (an evolution of SharePoint Guidance).

So given an POCO entity Cheese we also have a entity mapper like:

public class CheeseMapper : IEntityMapper<Cheese>
{
    //... methods implementing the interface (not important)
}

We could easily tell StructureMap to make CheeseMapper the default concrete type of IEntityMapper<Cheese> and repeat for all other entity mappers:

public class ARegistry : StructureMap.Configuration.DSL.Registry
{
     public ModelRegistry()
     {
         ForRequestedType<IEntityMapper<Cheese>>()
                 .TheDefaultIsConcreteType<CheeseMapper>()
     }
}

But this is error prone as I know I will forget to add the registry entry in StructureMap. And because our unit tests create minimal IoC containers for each fixture we aren’t going to pick this up until the integration test.

A better way is to register by convention:

public class ARegistry : StructureMap.Configuration.DSL.Registry
{
     public ModelRegistry()
     {
            Scan(
                    scan =>
                    {
                        scan.AssemblyContainingType(GetType());
                        scan.With<EntityMapperScanner>();
                    });
     }
}

public class EntityMapperScanner : ITypeScanner
{
    public void Process(Type type, PluginGraph graph)
    {
        if(!type.IsClass || type.IsAbstract)
           return;

        var selectedInterface =
               (from candidateInterface in type.GetInterfaces()
                where candidateInterface.IsGenericType
                where typeof(IEntityListMapper<>)
                            .IsAssignableFrom(
                                candidateInterface.GetGenericTypeDefinition()
                            )
                select candidateInterface).SingleOrDefault();

        if(selectedInterface != null)
            graph.Configure(r =>
                   {
                       r.ForRequestedType(selectedInterface)
                        .TheDefaultIsConcreteType(type);
                   });
    }
}

This code inspects each type in the assembly and for each type determines whether it implements the open generic type IEntityMapper<>. If it does, it takes the closed generic interface (i.e. IEntityMapper<Cheese>) as the service to register and the provided type as the implementation.

kick it on DotNetKicks.com

About these ads


Follow

Get every new post delivered to your Inbox.

%d bloggers like this: