Los Techies : Blogs about software and anything tech!

Advanced StructureMap: connecting implementations to open generic types


One pattern we’re starting to see more and more is the idea of connecting messages to handlers.  These messages might be domain command messages, ActionResult messages, and more.  Beyond messaging implementations, we start to see a more basic pattern start to emerge.  We have some interface describing a contract that happens to be generic:

public interface IFooService<T>
{
    void DoSomething(int value, T foo);
}

Now, if we weren’t doing IoC, and we needed a specific FooService for some type T, we’d have to know which type to get.  But you might start to see situations where you need an IFooService<T>, but you don’t really care about the T specifically:

public class SomethingThatUsesFoo<T>
{
    private readonly IFooService<T> _service;

    public SomethingThatUsesFoo(IFooService<T> service)
    {
        _service = service;
    }

    public void SomethingSpecific(T value)
    {
        _service.DoSomething(4, value);
    }
}

As you start to build more and more generic components, building out common infrastructure components, you’ll start to build more common services like these, that coordinate between a messages and their handlers.  In most cases like these, we’re not using generics for type safety, but rather for metadata to match up input types to output services.  A more concrete example on a real project looks like this:

public interface IHandler<TEvent> 
{
    void Handle(TEvent args);
}

This is an interface for domain events, where we’ll have handlers like:

public class OrderCanceledEvent
    : IHandler<OrderCanceledMessage>
{
    public void Handle(OrderCanceledMessage args)
    {
        // send an email or something
    }
}

Now the trick is, how do we instruct our Inversion of Control container to locate the right handler for the right event?  If you’re using StructureMap, it’s dirt, dirt simple.

Configuring StructureMap

Because we’re using StructureMap, we’ll be using a custom Registry to do our configuration.  To connect implementations, we want to make sure that any time we ask StructureMap for an IHandler<T>, it finds the concrete type of handler T.  In the above example, our common message routing code will ask for an IHandler<OrderCanceledMessage>, and the type located needs to be OrderCanceledEvent, because OrderCanceledEvent implements the IHandler<OrderCanceledMessage>.

Our Registry winds up being very simple:

public class HandlerRegistry : Registry
{
    public HandlerRegistry()
    {
        Scan(cfg =>
        {
            cfg.TheCallingAssembly();
            cfg.IncludeNamespaceContainingType<OrderCanceledEvent>();
            cfg.ConnectImplementationsToTypesClosing(typeof(IHandler<>));
        });
    }
}

To connect implementations to our open generic type of IHandler<T>, we use the ConnectImplementationsToTypesClosing method.  The other two are just directions telling StructureMap where to look for my handlers.  Typically, my registration code lives in the same assembly as the actual interfaces I’m registering, but you can also register by name.

But that’s it!  Very simple, one line to connect all of the handlers to their implementations.  I can verify this with a simple test:

[Test]
public void Should_connect_types()
{
    ObjectFactory.Initialize(init =>
    {
        init.AddRegistry<HandlerRegistry>();
    });

    ObjectFactory.AssertConfigurationIsValid();

    Debug.WriteLine(ObjectFactory.WhatDoIHave());

    var handler = ObjectFactory.GetInstance<IHandler<OrderCanceledMessage>>();

    handler.ShouldBeInstanceOf<OrderCanceledEvent>();
}

This test passes, and all is well in IoC land.

Embracing the container

At some point, users of IoC learn to stop caring and love the bomb container.  In our system, we have no less than ten usages of this method, meaning we have refactored a lot of common plumbing and coordinators into our infrastructure layer.  We can easily add new handlers, mappers, repositories, providers, commands, builders, invokers, etc., all with one line of configuration for each open type (NOT one per derived type/implementation).  It certainly opens the doors to new possibilities of separation of concerns between different pieces, we just happen to lean on the type system to route our information around.

Something to note here is that at no time did I need to describe the implementors.  StructureMap’s scanner merely found implementations of types closing IHandler<T>, and connected that concrete type to the requested type of IHandler<SpecificType>.  I’ve taken a look at the other containers, and frankly, I haven’t found any that match this level of simplicity in configuration.  But I’m not an expert on the other containers, so I’d love to be proven wrong!

Regardless, I absolutely love this pattern of usage in IoC.  It promotes a level of SOLID design that’s pretty tough to beat.  When folks talk about IoC only being useful in large or complex projects, I just don’t understand it.  Usages like this give me Separation of Concerns from the get-go, at almost zero cost.  If only other frameworks were built with this in mind

Kick It on DotNetKicks.com
Posted Dec 17 2009, 11:03 PM by bogardj
Filed under:

Comments

Eric Hauser wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-18-2009 12:18 AM

Windsor can handle this scenario as well.  James Kovacs has a write up:

www.devx.com/.../5

Kevin Miller wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-18-2009 12:21 AM

Great post Jimmy.

I am using the same technique to wire up message consumers (handlers) to be MassTransit message bus subscribers.

I love how simple it is to let the StructureMap scanner do the hard stuff.

Jeff Barnes wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-18-2009 6:23 AM

Thanks for the tip.  

I'm using a similar handler concept for a CQRS implementation with a WCF command service and query service that dispatch messages to handlers.  I'm also using StructureMap and had overlooked this feature.  This will be a *big* time saver.

bogardj wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-18-2009 8:40 AM

@Eric Hauser

That's not quite the same solution.  In that post, you're connecting an open generic interface to an open generic implementation.  In my example, you're connecting types that close the generic interface to its implementation.

For example, we have around 200 implementors of a Command<T>.  Registering all 200 unique implementors requires 1 line of StructureMap registration code.

Joshua Flanagan wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-18-2009 9:00 AM

@Eric - that Windsor example is a little different from what Jimmy is talking about. In James example, he is explicitly telling the Windsor which concrete (Repository<T>) type to use for the interface (IRepository<T>).

In the StructureMap example, Jimmy never explicitly tells the container which concrete type to use. This allows concrete types with different implementations such as OrderRepository and CustomerRepository, instead of just Repository<Order> and Repository<Customer> (which really have the same implementation).

uberVU - social comments wrote Social comments and analytics for this post
on 12-18-2009 10:55 AM

This post was mentioned on Twitter by jbogard: first in a series of advanced structuremap usage: http://bit.ly/4v7DLK

Elegant Code » Advanced Unity: Connecting Implementations to Open Generic Types wrote Elegant Code &raquo; Advanced Unity: Connecting Implementations to Open Generic Types
on 12-18-2009 6:46 PM

Pingback from  Elegant Code » Advanced Unity: Connecting Implementations to Open Generic Types

Paul Linton wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-18-2009 11:09 PM

What version of SM has ConnectImplementationsToTypesClosing?  Has it been released?

thanks

Sanjeev Agarwal wrote Daily tech links for .net and related technologies - December 19-21, 2009
on 12-19-2009 2:53 AM

Daily tech links for .net and related technologies - December 19-21, 2009 Web Development An Intro to

Alex Meyer-Gleaves wrote Registering open generic interface types in Autofac
on 12-20-2009 4:22 AM

Registering open generic interface types in Autofac

Eric Hauser wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-20-2009 9:00 AM

@Jimmy/Joshua

Correct, James article does not show how to register this scenario.  Here's an example using the registration API:

kernel.Register(AllTypes.Pick().FromAssembly(Assembly.GetExecutingAssembly()).BasedOn(typeof(ICommand<>)).WithService.Base());

bogardj wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-20-2009 2:52 PM

@Paul

This is in the trunk version, but will be in the upcoming 2.5.4 release.

@Eric

What does WithService.Base() do exactly?

Artem Govorov wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-20-2009 7:34 PM

>I’ve taken a look at the other containers, and frankly, I haven’t

>found any that match this level of simplicity in configuration.  But

>I’m not an expert on the other containers, so I’d love to be

>proven wrong!

I think you've described special case usage of more general auto registration feature. I have implemented general auto registration solution with nice fluent syntax for Unity (see here autoregistration.codeplex.com), Marcin Budny had created predicate for open generics to simplify registering them (marcinbudny.blogspot.com/.../unity-auto-registration.html). You can have a look and compare readability and simplicity.

Derek Greer wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-21-2009 8:51 AM

Very cool stuff.  I added this ability to an extension I wrote for Unity (see http://bit.ly/6FcCwl) after you first tweeted about it back in August, but its features like this that have made me consider StructureMap for future projects.

mob wrote re: Advanced StructureMap: connecting implementations to open generic types
on 12-25-2009 2:19 PM

This is pretty sweet. I just knocked down all my individual repository registrations to a few lines. Thanks for pointing this out.

Bj??rn Rochel’s weblog » Changes in StructureMap 2.5.4 wrote Bj??rn Rochel&#8217;s weblog &raquo; Changes in StructureMap 2.5.4
on 01-05-2010 4:45 PM

Pingback from  Bj??rn Rochel’s weblog » Changes in StructureMap 2.5.4

Jimmy Bogard wrote Advanced StructureMap: custom registration conventions for partially closed types
on 01-07-2010 11:59 AM

A while back, I highlighted an issue we ran into where I had basically partially closed generic types

Martin Nilsson wrote re: Advanced StructureMap: connecting implementations to open generic types
on 01-08-2010 6:52 AM

In Windsor:

//Registration

Kernel.Register(AllTypes.Of(typeof(IHandler<>)).FromAssembly(typeof(OrderCanceledEvent).Assembly).WithService.FirstInterface());

//Implementation

public class OrderCanceledEvent : IHandler<OrderCanceledMessage>

{}

//Usage

public class SomeClass(IHandler<OrderCanceledMessage> handler)

{}

Jimmy Bogard wrote Advanced StructureMap: Diagnosing problems
on 01-21-2010 11:21 PM

So you’ve set up StructureMap, got all your registries in a row, go to run the application, and you’re

corey coogan wrote re: Advanced StructureMap: connecting implementations to open generic types
on 01-22-2010 10:33 AM

Great article Jimmy.  One thing that I'm missing here is how you go about raising the events or calling the commands.

I assume you are using some sort of EventAggregator?  I love what you are showing here for registering the listeners and would like some closure on the big picture.

Jeremy D. Miller -- The Shade Tree Developer wrote StructureMap 2.6 (and 2.5.4) is Released!
on 02-04-2010 10:15 AM

Time flies when you’re having fun. It’s been almost a year since I’ve made an official

Add a Comment

(required)  
(optional)
(required)  
Remember Me?

Enter the numbers above:
Copyright Los Techies 2008, 2009. All rights reserved.
Powered by Community Server (Commercial Edition), by Telligent Systems