如何使用unity + UnityAutoRegistration打开通用装饰器链接

在阅读了关于命令处理程序装饰的这篇文章之后,今天在一个有趣的切线上走了。 我想看看我是否可以使用Unity而不是SimpleInjector来实现模式,到目前为止它certificate是非常困难的。

我要做的第一件事是安装UnityAutoRegistration来解析开放的通用ICommandHandler接口。 该方面的当前解决方案如下:

 Container = new UnityContainer().LoadConfiguration(); Container.ConfigureAutoRegistration() .ExcludeSystemAssemblies() .Include(type => type.ImplementsOpenGeneric(typeof(ICommandHandler)), (t, c) => c.RegisterType(typeof(ICommandHandler), t) ) .ApplyAutoRegistration() ; 

这适用于第一部分,解决任何单个 ICommandHandler 。 到目前为止,令人沮丧的是实施装饰处理程序。 一旦我添加第二个ICommandHandler作为装饰器,Unity就会抛出StackOverflowException。 我不太了解Unity内部,但我猜这是因为它无法确定要解析的实例 – 命令处理程序或命令处理程序装饰器 – 因为它们都实现了ICommandHandler接口。

谷歌搜索引导我首先阅读这篇文章 ,它解释了如何在我认为的蛮力方法中做到这一点。 我也找到了这些 相关的 页面,但没有一个是我问题的完整解决方案(我太无知了,不能自己解决)。

然后我找到了这篇文章,这似乎解决了我同样的担忧 。 然而,强大的解决方案并不能解决开放式generics问题。 目前,我们的大多数依赖项都是从.config文件中的unity部分加载的,所以我不想为每个处理程序或装饰器编写大量的编译代码。 似乎有某种UnityContainerExtension和DecoratorBuildStrategy是正确的方法,但我无法弄明白。 我现在已经玩了一段时间的powershell代码了,而且完全卡住了。 我尝试修改他的代码以考虑generics导致BadImageFormatExceptions(尝试加载具有不正确格式的程序。(HRESULT的exception:0x8007000B))。

我喜欢这样做以实现装饰器链接的想法,因为它很短,每个关注点只有1行:

 var container = new Container(); // Go look in all assemblies and register all implementations // of ICommandHandler by their closed interface: container.RegisterManyForOpenGeneric(typeof(ICommandHandler), AppDomain.CurrentDomain.GetAssemblies()); // Decorate each returned ICommandHandler object with an // TransactionCommandHandlerDecorator. container.RegisterDecorator(typeof(ICommandHandler), typeof(TransactionCommandHandlerDecorator)); // Decorate each returned ICommandHandler object with an // DeadlockRetryCommandHandlerDecorator. container.RegisterDecorator(typeof(ICommandHandler), typeof(DeadlockRetryCommandHandlerDecorator)); 

…但如果我不需要,我不想将我的容器从Unity更改为Simple Injector。

所以我的问题是我怎样才能使用统一(加上UnityAutoRegistration )实现开放式通用装饰器链接?

这在Unity中是等价的:

 // Go look in all assemblies and register all implementa- // tions of ICommandHandler by their closed interface: var container = new UnityContainer(); var handlerRegistrations = from assembly in AppDomain.CurrentDomain.GetAssemblies() from implementation in assembly.GetExportedTypes() where !implementation.IsAbstract where !implementation.ContainsGenericParameters let services = from iface in implementation.GetInterfaces() where iface.IsGenericType where iface.GetGenericTypeDefinition() == typeof(ICommandHandler<>) select iface from service in services select new { service, implementation }; foreach (var registration in handlerRegistrations) { container.RegisterType(registration.service, registration.implementation, "Inner1"); } // Decorate each returned ICommandHandler object with an // TransactionCommandHandlerDecorator. container.RegisterType(typeof(ICommandHandler<>), typeof(TransactionCommandHandlerDecorator<>), "Inner2", InjectionConstructor(new ResolvedParameter( typeof(ICommandHandler<>), "Inner1"))); // Decorate each returned ICommandHandler object with an // DeadlockRetryCommandHandlerDecorator. container.RegisterType(typeof(ICommandHandler<>), typeof(DeadlockRetryCommandHandlerDecorator<>), InjectionConstructor(new ResolvedParameter( typeof(ICommandHandler<>), "Inner2"))); 

我希望我能正确理解这个问题,我很想尝试让它工作,我不是Unity的专家,但我想的是一个更容易实现的解决方案,也更容易做到用不同的容器。 看起来支持开放generics以及其他类型的唯一方法是拥有2个独立的容器(1个用于开放式generics)和1个用于命令处理程序,这可能不是最好的方式,但它适用于Unity我假设其他人也会更容易。

所以我想出了这个:

我按如下方式创建了容器(您可以使用您的约定方法仍然确定处理程序容器)

 var container = new UnityContainer(); var container2 = new UnityContainer(); container2.RegisterType(typeof(ICommandHandler), typeof(QueryCommandHandler)); container.RegisterInstance("Handlers", container2); container.RegisterInstance(container); container.RegisterType(typeof(ICommandHandler<>), typeof(DecoratedHandler<>)); 

您将容器2包含Handlers作为命名实例。

然后我根据模式的要求创建了一个Generic基础装饰器类:

 public class DecoratorCommandHandler : ICommandHandler { private ICommandHandler inner; public DecoratorCommandHandler( ICommandHandler inner) { this.inner = inner; } public virtual void Handle(TCommand command) { this.inner.Handle(command); } } 

其次,我创建了另一个Generic Handler,它将包装你想要为你的解决方案做的所有装饰,在这里你将为TryCatch / Caching / Transactions或者你想要应用于每个命令处理程序的任何其他内容添加装饰:

 public class DecoratedHandler : DecoratorCommandHandler { public DecoratedHandler(UnityContainer container) : base(BuildInner(container)) { } private static ICommandHandler BuildInner( UnityContainer container) { var handlerContainer = container.Resolve("Handlers"); var commandHandler = handlerContainer.Resolve>(); return new TryCatchCommandHandler(commandHandler); } } 

您会注意到第一个内部按照您请求的那个解析实际命令处理程序,如QueryCommandHandlerUpdateCommandHandlerExecuteCommandHandler或处理细节的任何人。 然后用你想要的所有装饰器包裹起来。

然后我能够以正确的方式解析正确的处理程序:

 ICommandHandler handler = container.Resolve>(); var cmd = new QueryCommand(); handler.Handle(cmd); 

希望这可以帮助