如何使用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+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);
    }
}

您会注意到,第一个内部根据您请求的命令处理程序(如QueryCommandHandlerUpdateCommandHandlerExecuteCommandHandler或任何处理细节的程序)解析实际命令处理程序。然后被所有你想要的装饰器所包裹,所有这些装饰器都是公共的。

然后我能够解决正确的处理程序,以正确的方式装饰:

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

希望这能帮助