如何使用 unity + UnityAutoRegistration 进行开放式通用装饰器链接

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)实现开放通用装饰器链接?

Ste*_*ven 5

这在 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)