在StructureMap 3中使用Open Generics进行装饰器拦截

时间:2015-09-22 16:40:15

标签: c# generics structuremap structuremap3

我有一个项目正在使用装饰器约定来通过截取StructureMap 2.6中的开放泛型类型来包装带有日志装饰器的命令处理程序。但是,我很难找到在StructureMap 3中实现等效功能的最佳方法,以便我可以完成升级。

这是StructureMap 2.6的代码。首先,在我的IoC类中,我设置了一个扫描策略来解析命令处理程序:

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

接下来,我有一个装饰器约定,它被添加到IoC的扫描约定中,用于连接装饰器拦截:

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>()));
            }
        }
    }
}

然后,我们有一个命令总线,它将一个特定的命令映射到一个命令处理程序,并在日志装饰器(包装命令处理程序)上调用Execute方法,后者依次调用装饰器内部命令的Execute方法:

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);
    }
}

我已经能够在StructureMap 3中通过用拦截器策略替换我的装饰器约定并在IoC类中添加拦截器策略来完成这项工作。

这是拦截器政策:

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<>));
    }

以下是将其添加到IoC的拦截器策略中的代码:

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

但是,当我调用Container.GetInstance(在我的CommandBus中)时,它返回匹配的Command Handler实现而不是Command Logging装饰器。如果我调用Container.GetAllInstances,它将返回实现(第一个)和装饰器(第二个)。

所以,现在,我能够做到这一点的唯一方法是,如果我明确选择从Container.GetAllInstances返回的第二个项目,或者过滤结果并使用反射选择装饰器。这是一个例子:

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);
    }
}

然而,这似乎是一个非常难看的解决方案。显然必须有一些我不知道的东西。首先,当我明确添加装饰器拦截策略时,为什么Container.GetInstance返回实现而不是装饰器?第二,我是否应该更好地完成这项工作?

非常感谢任何想法或建议!

1 个答案:

答案 0 :(得分:4)

在StructureMap代码库中查看这个非常相似的示例(我刚刚写的),以获取使用具有泛型类型的装饰器的示例:https://github.com/structuremap/structuremap/blob/b405d8f752b45ac250f057d9e3de8554f2a7f40f/src/StructureMap.Testing/Bugs/OpenGenericDecorator_question.cs