18 Dec
2009

Advanced Unity: Connecting Implementations to Open Generic Types

Jimmy Bogard has an excellent post called “Advanced StructureMap: connecting implementations to open generic types” which he uses the StructureMap IOC container to connect messages to handlers.

This is something I have been using in my codebase to handle domain events, as well as a publish/subscribe mechanism for WCF message handling. I learned about this Handler<T>(or Consumer<T>) approach from the MassTransit codebase. For those that don’t know about MassTransit, it is lean service bus implementation for building loosely coupled applications using the .NET framework. I highly recommend checking it out.

Though I am a big fan of StructureMap, I have also use the Unity dependency injection container to resolve my handlers/consumers.

 

The Open Generic Handler

As Jimmy described in his post, there is an generic interface describing a contract

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

The Implementation

The interface is implemented specifying the type (the Domain Event) for the interface contract

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

Resolving the Handler

When a given domain event occurs, the handler(s) is resolved for the given event, the “T”, or OrderCancelledMessage.

var handler = container.Resolve<IHandler<OrderCanceledMessage>>();

Configuring Unity

There are a few ways to do this with Unity. I will show how to do it using a Unity Extension. Since this is fairly trivial, you could easily do it with a regular old Extension method, which would result in less setup code.

Basically the extension going to do is drill into the impl type and extract the interface that matches the passed in generic, and register it in the container.

The Unity Extension
public class OpenGenericExtension : UnityContainerExtension, IOpenGenericExtension
{
    protected override void Initialize()
    {
       
    }
 
    public void RegisterClosedImpl<T>(Type openGenericInterface)
    {
        var closedType = typeof(T);
 
        closedType.GetInterfaces()
                  .Where(x => x.IsGenericType)
                  .Where(x => x.GetGenericTypeDefinition() == openGenericInterface)
                  .ToList()
                  .ForEach(x => Container.RegisterType(x, closedType));
    }
}

public interface IOpenGenericExtension : IUnityContainerExtensionConfigurator
{
    void RegisterClosedImpl<T>(Type openInterface);
}

 

The Test

Make it go.

[Test]
public void should_connect_types()
{
    var container = new UnityContainer();
    container.AddNewExtension<OpenGenericExtension>()
             .Configure<IOpenGenericExtension>()
             .RegisterClosedImpl<OrderCanceledEvent>(typeof(IHandler<>));
 
    var handler = container.Resolve<IHandler<OrderCanceledMessage>>();
 
    Assert.AreEqual(handler.GetType(), typeof(OrderCanceledEvent));
}

8 thoughts on “Advanced Unity: Connecting Implementations to Open Generic Types

  1. Jarod,

    Watch your check in RegisterClosedImpl. The code is too naive. Sometimes IsGenericType doesn’t work and you also need to check to see if it has generic parameters. Plus, you need to walk it up the base type tree. Plus, you need to consider if the open type is an abstract class instead of an interface. I had to patch some of this on SM tonight.

    I will say though, that this is NOT a full equivalent to the SM conventional registration. Consider your effort with the Unity way versus the StructureMap Convention method. Advantage StructureMap;-)

  2. Hello,
    what about registering the same handler for subtypes of OrderCanceledMessage, like imaginary AbnormalOrderCanceledMessage? Shouldn’t the same handler be used for them as well, or maybe you want it to be as explicit as it can be (with no handling of message’s subtypes):>

  3. @Jeremy thank you for your input, the base class and abstract class scenarios are especially worth pointing out. Honestly I have not had to deal with those cases. I just stick to a simple Interface convention for messaging/domain events. This impl seems to solve my problems.

    I can understand there is certainly quite a bit more to worry about when building a framework like SM.

  4. @Szymon You could extend the API to register the base types of ‘closedType’ to the same handler if you wanted. I do not use inheritance for my domain events, ever. In my opinion, when using a list based router its important for the domain event/message to be explicit. Sematically these are two different ‘events’, its either AbnormalOrderCanceled or its OrderCanceled, not both.

    In your scenario I would have different handlers for both events. If an AbnormalOrderCanceled also needed to run the OrderCanceled Handlers, I would just publish the OrderCanceled event in AbnormalOrderCanceled Handler. An added benefit is that I have more control over whether to proceed to the next step. This quickly gets in to more elegant workflow solutions such as routing slips and sagas.

    Some psuedo code: (this impl is beyond this post and more service bus specific. hopefully it is useful for some)

    public class AbnormalOrderCanceledHandler : Handler
    {
    //pretty much all handlers get a reference back to the bus
    IServiceBus _serviceBus;

    AbnormalOrderCanceledHandler(IServiceBus serviceBus)
    {
    _serviceBus = serviceBus
    }

    public void Handle(AbnormalOrderCanceledMessage message)
    {
    // The abnormal parts of this event were handled successfully
    // so move on to OrderCanceled
    OrderCanceledMessage orderCancelledMessage = new OrderCanceledMessageBuilder(message);
    _serviceBus.Publish(orderCancelledMessage);
    }
    }

  5. @Jarod Ferguson
    Thank you for your answer. Your solution does make sense:) I could even imagine wiring this using an explicit interface like: IRaiseBaseTypeMessage for a message type but then, it would be (once again) not explicit in terms of handlers as the information would be stored in definition of the message type. To sum up: IF “it’s important for the domain event/message to be explicit” THEN your proposal is truly correct 🙂

Comments are closed.