Create your own Auto Registration Convention with StructureMap

In StructureMap 2.5 I added the ability to make “auto registration” policies that would enable StructureMap to just scan an assembly and figure out how to register types on its own. This is yet one more example of the new trend in .Net circles for adopting “Convention over Configuration” to make our work go faster.  There’ll be a backlash against that sometime soon, but for now, I think it’s a significant trend to watch play out in the .Net OSS space.

This blog post is excerpted from the online documentation as http://structuremap.sourceforge.net/ScanningAssemblies.htm.  The one and only Laribee asked me to write some samples of auto registration.  My respect and affection for Dave is so strong that it only took me 6 weeks to get around to it.

 

StructureMap 2.5+ adds the ability to do convention based type registration. 
Convention
over Configuration
is becoming increasingly more common as a way to reduce
the overhead of using configuration intensive tools.  Type registration
conventions can be mixed and matched with explicit configuration.

The Default Convention

When using StructureMap over the last couple years I saw a lot of type
registrations like:

            Start<ICommandExecutor>().TheDefaultIsConcreteType<CommandExecutor>();

There was a reoccuring pattern.  “Foo” was the default implementation of
“IFoo.”  The “I” moniker is the last vestige of Hungarian Notation left in
my coding style, but it refuses to go away, so let’s just take advantage of that
naming convention with a new DefaultConvention. 

Let’s say that we have a class and interface like:

    public interface
IConvention

    {

    }

 

    public class
Convention :
IConvention

    {

    }

To make Convention be the default type for IConvention, I can simply scan the
assembly containing these types with the new default convention:

           
var
container = new
Container(registry =>

            {

               
registry.Scan(x =>

                {

                   
x.TheCallingAssembly();

                   
x.With<DefaultConventionScanner>();

                });

            });

 

            container.GetInstance<IConvention>().ShouldBeOfType<Convention>();


Registering Types by Name

One of the simplest, but yet most useful, ways to use convention based auto
registration is just to add all concrete types that can be cast to a certain
PluginType and use a convention to determine the Instance name.  The first
place I used this functionality was at the beginning of our MVC project. 
Let’s say that we name our Controller classes like this:

    public interface
IController{}

    public class
AddressController :
IController{}

    public class
SiteController :
IController{}

As a convention, we’d like to register AddressController as the “Address” of
IController, and “Site” or IController like this:

            container =
new Container(x
=>

            {

               
x.ForRequestedType<IController>().AddInstances(o
=>

                {

                   
o.OfConcreteType<AddressController>().WithName(“Address”);

                   
o.OfConcreteType<SiteController>().WithName(“Site”);

                })

            });

Over 50+ Controller classes, that coding is going to get extremely tedious. 
Let’s use a convention instead:

            container =
new Container(x
=>

            {

               
x.Scan(o =>

                {

                   
o.TheCallingAssembly();

                   
o.AddAllTypesOf<IController>().NameBy(type
=> type.Name.Replace(“Controller”,
“”));

                });

            });

 

           
foreach
(var instance
in container.Model.InstancesOf<IController>())

            {

               
Debug.WriteLine(instance.Name +
” is “ + instance.ConcreteType.Name);

            }

When this code is executed, we get this output:

Address is AddressController
Site is SiteController

Here’s the unit test code for this functionality:

        [Test]

        public
void can_find_objects_later_by_name()

        {

            container.GetInstance<IController>(“Address”)

               
.ShouldBeOfType<AddressController>();

 

            container.GetInstance<IController>(“Site”)

               
.ShouldBeOfType<SiteController>();

        }


Creating and Using Your Own Convention

To write your own auto registration convention, write a concrete class that
implements the ITypeScanner interface:

    public interface
ITypeScanner

    {

        void Process(Type type,
PluginGraph
graph);

    }

Inside a custom ITypeScanner, you add types to a Container by modifying the
PluginGraph object that is passed into the TypeScanner.  While having
access to the PluginGraph allows you to make any possible type of configuration
change (PluginGraph is the
Semantic Model
behind both the Registry DSL and the Xml configuration), in practice you
probably only care about these three methods:

        /// <summary>

        /// Adds the concreteType as an Instance of the
pluginType

        /// </summary>

        /// <param
name=”pluginType”></param>

        /// <param
name=”concreteType”></param>

        void AddType(Type pluginType,
Type concreteType);

 

        /// <summary>

        /// Adds the concreteType as an Instance of the
pluginType with a name

        /// </summary>

        /// <param
name=”pluginType”></param>

        /// <param
name=”concreteType”></param>

        /// <param
name=”name”></param>

        void AddType(Type pluginType,
Type concreteType,
string name);

 

        /// <summary>

        /// Add the pluggedType as an instance to any configured
pluginType where pluggedType

        /// could be assigned to the pluginType

        /// </summary>

        /// <param
name=”pluggedType”></param>

        void AddType(Type pluggedType);

Here’s a sample type convention from my current project:

    ///
<summary>/span>

    /// This
TypeScanner looks for any concrete class that implements

    /// an
IFlattenerFor
<T> interface, and registers that type

    /// against the
closed IFlattenerFor
<T> interface,

    /// i.e.
IFlattenerFor
<Address> or IFlattenerFor<Site>

    ///
</summary>

    public class
DtoFlattenerConventionScanner :
ITypeScanner

    {

        public
void Process(Type
type, PluginGraph graph)

        {

           
Type
interfaceType = type.FindInterfaceThatCloses(typeof (IFlattenerFor<>));

            if
(interfaceType != null)

            {

               
graph.AddType(interfaceType, type);

            }

        }

&    }

You apply the custom ITypeScanner by simply calling With() inside of a Scan()
block:

            Scan(x =>

            {

               
// scan with a custom ITypeScanner

               
x.With<DomainEntityAliaser>();

               
x.With<QueueItemMappingScanner>();

            });

About Jeremy Miller

Jeremy is the Chief Software Architect at Dovetail Software, the coolest ISV in Austin. Jeremy began his IT career writing "Shadow IT" applications to automate his engineering documentation, then wandered into software development because it looked like more fun. Jeremy is the author of the open source StructureMap tool for Dependency Injection with .Net, StoryTeller for supercharged acceptance testing in .Net, and one of the principal developers behind FubuMVC. Jeremy's thoughts on all things software can be found at The Shade Tree Developer at http://codebetter.com/jeremymiller.
This entry was posted in StructureMap. Bookmark the permalink. Follow any comments here with the RSS feed for this post.
  • nuff

    nice code formatting.

  • Adp Upasana

    Would you please remove my mail id from “http://codebetter.com/raymondlewallen/2005/02/08/advantages-of-an-object-oriented-approach-for-new-programmers/”
    please sir… 

  • http://www.backlinkschecker.ws/backlinks/buylinks.html Purchase High PR Backlink

    These are wonderful

  • Anonymous

    Thank you,i have learnt a lot,it is good!

  • Sander

    cna you help me

    Msn : sanderstr11@live.nl

  • http://www.best-registrycleaner.net Best Registry Cleaner

    Net circles for adopting “Convention over Configuration” to make our work go faster.

  • http://codebetter.com/blogs/jeremy.miller Jeremy D. Miller

    @Jon,

    As long as you’re happy with what you got, I say leave it alone.

  • http://jonkruger.com/blog Jon Kruger

    I’ve been doing this kind of thing inside my Registry classes (in the configure() method)… I’ll use reflection to figure out how to auto-wire things using ForRequestedType().TheDefaultIsConcreteType().

    Is this way you’ve documented somehow better than what I’m doing or is it just another way to do it?

    Jon

  • http://www.adverseconditionals.com Harry M

    whoops I meant
    Register(interfaceType, implementerType);

  • http://www.adverseconditionals.com Harry M

    I love this stuff, the concept of auto registering stuff etc etc is awesome, but I am finding all the auto-registration APIs a bit annoying. I need one for Fluent NHib, one for my IoC container, one for the one I use at home etc. etc.

    If you have a simple Register(Type interface, Type implementer) and LINQ you can just write something like
    foreach (var interfaceType in typeof(IService).Assembly.GetTypes().Where(MyConvention)
    {
    foreach (var implementerType in classes.GetTypes().Where(t => interfaceType.IsAssignableFrom(t)))
    {
    Register(interfaceType, interfaceType.FullName);
    }
    }

    OK so its not as terse, but everyone on the team can tell at a glance what it’s doing and you can customise it to your hearts content.

  • http://blog@danielfernandes.net Daniel Fernandes

    Good to see StructureMap moving along so quickly.
    I will definitely give it a go next time I start a project, I’m finding it frustrating there hasn’t been a realease of CastleWindsor in a long while.