如何使用unity+UnityAutoRegistration进行开放式泛型装饰器链接
本文关键字:链接 泛型 开放式 何使用 unity+UnityAutoRegistration | 更新日期: 2023-09-27 18:28:47
在阅读了这篇关于命令处理程序装饰的文章后,今天开始了一个有趣的话题。我想看看是否可以使用Unity而不是SimpleInjector来实现该模式,但到目前为止,这非常困难。
我必须做的第一件事是安装UnityAutoRegistration来解析打开的通用ICommandHandler<TCommand>
接口。目前这方面的解决方案如下:
Container = new UnityContainer().LoadConfiguration();
Container.ConfigureAutoRegistration()
.ExcludeSystemAssemblies()
.Include(type => type.ImplementsOpenGeneric(typeof(ICommandHandler<>)),
(t, c) => c.RegisterType(typeof(ICommandHandler<>), t)
)
.ApplyAutoRegistration()
;
这适用于第一部分,解决任何单个ICommandHandler<TCommand>
。到目前为止,令人沮丧的是实现了一个装饰处理程序。一旦我添加第二个ICommandHandler<TCommand>
作为装饰器,Unity就会抛出StackOverflowException。我对Unity内部的了解还不够,但我猜这是因为它无法确定要解析到哪个实例——命令处理程序或命令处理程序装饰器——因为两者都实现了ICommandHandler<TCommand>
接口。
在谷歌上搜索后,我首先看到了这篇文章,它解释了如何用我认为是蛮力的方法来做这件事。我也找到了这些相关的页面,但没有一个能完全解决我的问题(我太无知了,无法自己解决)。
然后我找到了这篇文章,它似乎解决了我同样的担忧。然而,牛肉的解决方案并没有考虑到处理开放泛型。目前,我们的大多数依赖项都是从.config文件中的unity部分加载的,所以我不想为每个处理程序或装饰程序编写大量的编译代码。拥有某种UnityContainerExtension和DecoratorBuildStrategy似乎是正确的做法,但我不明白。我已经玩了一段时间的牛肉的代码,现在完全卡住了。我试图修改他的代码以考虑泛型,这导致了BadImageFormatException(试图加载格式不正确的程序。(HRESULT中的异常:0x8007000B))。
我喜欢这样做来实现装饰器链接的想法,因为它很短,每个关注点只有一行:
var container = new Container();
// Go look in all assemblies and register all implementations
// of ICommandHandler<T> by their closed interface:
container.RegisterManyForOpenGeneric(typeof(ICommandHandler<>),
AppDomain.CurrentDomain.GetAssemblies());
// Decorate each returned ICommandHandler<T> object with an
// TransactionCommandHandlerDecorator<T>.
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>));
// Decorate each returned ICommandHandler<T> object with an
// DeadlockRetryCommandHandlerDecorator<T>.
container.RegisterDecorator(typeof(ICommandHandler<>),
typeof(DeadlockRetryCommandHandlerDecorator<>));
但如果不必要的话,我不想把我的容器从Unity改为Simple Injector。
所以我的问题是,我如何使用unity(加上UnityAutoRegistration
)来实现开放的泛型装饰器链接?
这在Unity:中是等效的
// Go look in all assemblies and register all implementa-
// tions of ICommandHandler<T> 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<T> object with an
// TransactionCommandHandlerDecorator<T>.
container.RegisterType(typeof(ICommandHandler<>),
typeof(TransactionCommandHandlerDecorator<>),
"Inner2",
InjectionConstructor(new ResolvedParameter(
typeof(ICommandHandler<>), "Inner1")));
// Decorate each returned ICommandHandler<T> object with an
// DeadlockRetryCommandHandlerDecorator<T>.
container.RegisterType(typeof(ICommandHandler<>),
typeof(DeadlockRetryCommandHandlerDecorator<>),
InjectionConstructor(new ResolvedParameter(
typeof(ICommandHandler<>), "Inner2")));
我希望我能正确理解这个问题,我很想尝试让它发挥作用,我绝不是Unity的专家,但我正在考虑一个更容易实现的解决方案,而且使用不同的容器也更容易。支持开放泛型和其他类型的唯一方法似乎是有两个单独的容器(1个用于开放泛型)和一个用于命令处理程序,这可能不是最好的方法,但它与Unity一起使用,我想与其他容器一起使用也会更容易。
所以我想出了这个:
我创建了如下容器(你可以使用你的约定方法,仍然不能确定处理容器)
var container = new UnityContainer();
var container2 = new UnityContainer();
container2.RegisterType(typeof(ICommandHandler<QueryCommand>),
typeof(QueryCommandHandler));
container.RegisterInstance("Handlers", container2);
container.RegisterInstance(container);
container.RegisterType(typeof(ICommandHandler<>),
typeof(DecoratedHandler<>));
您可以看到容器2包含作为命名实例的Handlers。
然后,我只是根据模式的要求创建了一个Generic基装饰器类:
public class DecoratorCommandHandler<TCommand>
: ICommandHandler<TCommand>
{
private ICommandHandler<TCommand> inner;
public DecoratorCommandHandler(
ICommandHandler<TCommand> inner)
{
this.inner = inner;
}
public virtual void Handle(TCommand command)
{
this.inner.Handle(command);
}
}
其次,我创建了另一个通用处理程序,它将包装您想为解决方案做的所有装饰,在这里,您将添加TryCatch/Cacheching/Transactions的装饰,或者您想应用于每个命令处理程序的任何其他东西:
public class DecoratedHandler<TCommand>
: DecoratorCommandHandler<TCommand>
{
public DecoratedHandler(UnityContainer container)
: base(BuildInner(container))
{
}
private static ICommandHandler<TCommand> BuildInner(
UnityContainer container)
{
var handlerContainer =
container.Resolve<UnityContainer>("Handlers");
var commandHandler =
handlerContainer.Resolve<ICommandHandler<TCommand>>();
return new TryCatchCommandHandler<TCommand>(commandHandler);
}
}
您会注意到,第一个内部根据您请求的命令处理程序(如QueryCommandHandler
、UpdateCommandHandler
、ExecuteCommandHandler
或任何处理细节的程序)解析实际命令处理程序。然后被所有你想要的装饰器所包裹,所有这些装饰器都是公共的。
然后我能够解决正确的处理程序,以正确的方式装饰:
ICommandHandler<QueryCommand> handler =
container.Resolve<ICommandHandler<QueryCommand>>();
var cmd = new QueryCommand();
handler.Handle(cmd);
希望这能帮助