dan*_*wig 4 .net c# dependency-injection unity-container simple-injector
读完这篇关于命令处理程序装饰的文章后,今天谈到了一个有趣的话题。我想看看是否可以使用 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()
;
Run Code Online (Sandbox Code Playgroud)
这适用于第一部分,解决任何单个 ICommandHandler<TCommand>
. 到目前为止,令人沮丧的是实现装饰处理程序。一旦我添加第二个ICommandHandler<TCommand>
装饰器,Unity 就会抛出 StackOverflowException。我对 Unity 内部了解不够,但我猜这是因为它无法确定要解析到哪个实例(命令处理程序或命令处理程序装饰器),因为两者都实现了该ICommandHandler<TCommand>
接口。
谷歌搜索让我首先找到了这篇文章,它解释了如何用我认为的蛮力方法来做到这一点。我还找到了这些 相关 页面,但没有一个是解决我的问题的完整解决方案(而且我太无知了,无法自己弄清楚)。
然后我找到了这篇文章,它似乎解决了我同样的担忧。然而,beefy 的解决方案并没有考虑处理开放仿制药。目前,我们的大多数依赖项都是从 .config 文件中的统一部分加载的,因此我不想为每个处理程序或装饰器编写大量编译代码。似乎拥有某种 UnityContainerExtension 和 DecoratorBuildStrategy 是正确的方法,但我无法弄清楚。我已经玩了一段时间的 Beefy 代码,但完全陷入困境。我尝试修改他的代码以考虑泛型导致了 BadImageFormatExceptions(尝试加载格式不正确的程序。(来自 HRESULT 的异常:0x8007000B))。
我喜欢这样做来实现装饰器链接的想法,因为它很短,并且每个关注点只有 1 行:
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<>));
Run Code Online (Sandbox Code Playgroud)
...但如果不需要的话,我不想将容器从 Unity 更改为 Simple Injector。
所以我的问题是如何使用 unity (plus 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")));
Run Code Online (Sandbox Code Playgroud)