Search code examples
c#genericsstructuremapstructuremap3

Decorator Interception with Open Generics in StructureMap 3


I have a project that is using a decorator convention to wrap command handlers with logging decorators via interception of open generic types in StructureMap 2.6. However, I'm having difficulty figuring out the best way to implement the equivalent functionality in StructureMap 3 so that I can complete the upgrade.

Here's the code from StructureMap 2.6. First, in my IoC class I have a scanning policy set up to resolve the command handlers:

scan.ConnectImplementationsToTypesClosing(typeof(ICommandHandler<>));

Next, I have a decorator convention, which is added to the IoC's scanning conventions, that wires up the decorator interception:

public class CommandLoggingDecoratorConvention : IRegistrationConvention
{
    public void Process(Type type, Registry registry)
    {
        var interfaceTypes = type.GetInterfaces();

        foreach (var interfaceType in interfaceTypes)
        {
            if (interfaceType.IsGenericType
                && interfaceType.GetGenericTypeDefinition() == typeof(ICommandHandler<>))
            {
                var arguments = interfaceType.GetGenericArguments();

                var closedType = typeof(CommandHandlerLoggingDecorator<>)
                    .MakeGenericType(arguments);

                registry.For(interfaceType)
                    .EnrichWith((c, p) => Activator.CreateInstance(
                        closedType, 
                        p, 
                        c.GetInstance<IMessageLoggingHelper>(), 
                        c.GetInstance<ILog>()));
            }
        }
    }
}

Then, we have a command bus which maps a specific command to a command handler and calls the Execute method on the logging decorator (which is wrapping the command handler) which in turns calls the Execute method on the command inside of the decorator:

public class CommandBus : ICommandBus
{
    public static IContainer Container;

    public void Execute(ICommand command)
    {
        var handlerType = typeof (ICommandHandler<>)
            .MakeGenericType(command.GetType());

        dynamic handler = Container
            .GetAllInstances(handlerType)
            .Cast<dynamic>()
            .Single();

        handler.Execute((dynamic) command);
    }
}

I have been able to make this work in StructureMap 3 by replacing my decorator convention with an interceptor policy and adding the interceptor policy in the IoC class.

Here's the interceptor policy:

public class CommandLoggingDecoratorPolicy : IInterceptorPolicy
{
    public string Description { get; private set; }

    public IEnumerable<IInterceptor> DetermineInterceptors(Type pluginType, Instance instance)
    {
        if (pluginType == typeof (ICommandHandler<>))
            yield return new DecoratorInterceptor(
                typeof(ICommandHandler<>),
                typeof(CommandHandlerLoggingDecorator<>));
    }

And here's the code that adds it to the IoC's interceptor policies:

x.Policies.Interceptors(new CommandLoggingDecoratorPolicy());

However, when I call Container.GetInstance (in my CommandBus) it returns the matching Command Handler implementation instead of the Command Logging decorator. If I call Container.GetAllInstances, it returns both the implementation (first) and the decorator (second).

So, right now, the only way I am able to make this work is if I either explicitly choose the second item returned from Container.GetAllInstances or filter the results and choose the decorator using reflection. Here's an example:

public class CommandBus : ICommandBus
{
    public static IContainer Container;

    public void Execute(ICommand command)
    {
        var handlerType = typeof (ICommandHandler<>)
            .MakeGenericType(command.GetType());

        var handlers = Container
            .GetAllInstances(handlerType)
            .Cast<dynamic>();

        var handler = handlers.ToList()[1];

        handler.Execute((dynamic) command);
    }
}

However, this seems like a pretty ugly solution. There clearly must be something that I'm missing. First, why is Container.GetInstance returning the implementation rather than the decorator when I've explicitly added a decorator interception policy? Second, is there a better way that I should be doing this altogether?

Any ideas or suggestions would be greatly appreciated!


Solution

  • See this remarkably similar sample (that I just wrote) in the StructureMap codebase for an example of using decorators with generic types: https://github.com/structuremap/structuremap/blob/b405d8f752b45ac250f057d9e3de8554f2a7f40f/src/StructureMap.Testing/Bugs/OpenGenericDecorator_question.cs