StructureMap 3 中开放泛型的装饰器拦截
Decorator Interception with Open Generics in StructureMap 3
我有一个项目正在使用装饰器约定通过拦截 StructureMap 2.6 中的开放通用类型来使用日志装饰器包装命令处理程序。但是,我很难找出在 StructureMap 3 中实现等效功能的最佳方法,以便我可以完成升级。
这是来自 StructureMap 2.6 的代码。首先,在我的 IoC class 中,我设置了扫描策略来解析命令处理程序:
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);
}
}
通过用拦截器策略替换我的装饰器约定并在 IoC 中添加拦截器策略,我已经能够在 StructureMap 3 中完成这项工作 class。
拦截器策略如下:
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 中)时,它 returns 匹配的命令处理程序实现而不是命令记录装饰器。如果我调用 Container.GetAllInstances,它 returns 实现(第一个)和装饰器(第二个)。
所以,现在,我能够完成这项工作的唯一方法是,如果我明确选择从 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 在我明确添加装饰器拦截策略时返回实现而不是装饰器?其次,有没有更好的方法让我完全做到这一点?
如有任何想法或建议,我们将不胜感激!
请参阅 StructureMap 代码库中这个非常相似的示例(我刚刚编写的示例),以了解使用具有泛型类型的装饰器的示例:https://github.com/structuremap/structuremap/blob/b405d8f752b45ac250f057d9e3de8554f2a7f40f/src/StructureMap.Testing/Bugs/OpenGenericDecorator_question.cs
我有一个项目正在使用装饰器约定通过拦截 StructureMap 2.6 中的开放通用类型来使用日志装饰器包装命令处理程序。但是,我很难找出在 StructureMap 3 中实现等效功能的最佳方法,以便我可以完成升级。
这是来自 StructureMap 2.6 的代码。首先,在我的 IoC class 中,我设置了扫描策略来解析命令处理程序:
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);
}
}
通过用拦截器策略替换我的装饰器约定并在 IoC 中添加拦截器策略,我已经能够在 StructureMap 3 中完成这项工作 class。
拦截器策略如下:
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 中)时,它 returns 匹配的命令处理程序实现而不是命令记录装饰器。如果我调用 Container.GetAllInstances,它 returns 实现(第一个)和装饰器(第二个)。
所以,现在,我能够完成这项工作的唯一方法是,如果我明确选择从 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 在我明确添加装饰器拦截策略时返回实现而不是装饰器?其次,有没有更好的方法让我完全做到这一点?
如有任何想法或建议,我们将不胜感激!
请参阅 StructureMap 代码库中这个非常相似的示例(我刚刚编写的示例),以了解使用具有泛型类型的装饰器的示例:https://github.com/structuremap/structuremap/blob/b405d8f752b45ac250f057d9e3de8554f2a7f40f/src/StructureMap.Testing/Bugs/OpenGenericDecorator_question.cs