自定义 Autofac 的组件分辨率/通用协方差/逆变问题 [英] Customizing Autofac's component resolution / Issue with generic co-/contravariance

查看:18
本文介绍了自定义 Autofac 的组件分辨率/通用协方差/逆变问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,抱歉问题标题含糊不清.我想不出更精确的了.

First, sorry for the vague question title. I couldn't come up with a more precise one.

鉴于这些类型:

                                                     { TCommand : ICommand }
       «interface»                   «interface»    /
      +-----------+         +----------------------/----+
      | ICommand  |         | ICommandHandler<TCommand> |
      +-----------+         +---------------------------+
            ^               | Handle(command: TCommand) |
            |               +---------------------------+
            |                              ^
            |                              |
      +------------+            +-------------------+
      | FooCommand |            | FooCommandHandler |
      +------------+            +-------------------+
            ^
            |
   +-------------------+
   | SpecialFooCommand |
   +-------------------+

我想编写一个方法 Dispatch 接受任何命令并将其发送到适当的 ICommandHandler<>.我认为使用 DI 容器 (Autofac) 可能会大大简化从命令类型到命令处理程序的映射:

I would like to write a method Dispatch that accepts any command and sends it to an appropriate ICommandHandler<>. I thought that using a DI container (Autofac) might greatly simplify the mapping from a command's type to a command handler:

void Dispatch<TCommand>(TCommand command) where TCommand : ICommand
{
    var handler = autofacContainer.Resolve<ICommandHandler<TCommand>>();
    handler.Handle(command);
}

假设 DI 容器知道上面显示的所有类型.现在我打电话:

Let's say the DI container knows about all the types shown above. Now I'm calling:

Dispatch(new SpecialFooCommand(…));

实际上,这将导致 Autofac 抛出 ComponentNotRegisteredException,因为没有可用的 ICommandHandler.

In reality, this will result in Autofac throwing a ComponentNotRegisteredException, since there is no ICommandHandler<SpecialFooCommand> available.

然而,理想情况下,我仍然希望 SpecialFooCommand 由可用的最接近匹配的命令处理程序来处理,即.通过上面例子中的 FooCommandHandler.

Ideally however, I would still want a SpecialFooCommand to be handled by the closest-matching command handler available, ie. by a FooCommandHandler in the above example.

Autofac 是否可以为此进行定制,也许可以使用自定义注册源?

Can Autofac be customized towards that end, perhaps with a custom registration source?

PS: 我知道可能存在协变/逆变的基本问题(如下例所示),唯一的解决方案可能是不完全使用泛型......但如果可能的话,我想坚持使用泛型类型.

P.S.: I understand that there might be the fundamental problem of co-/contravariance getting in the way (as in the following example), and that the only solution might be one that doesn't use generics at all... but I would want to stick to generic types, if possible.

ICommandHandler<FooCommand> fooHandler = new FooCommandHandler(…);
ICommandHandler<ICommand> handler = fooHandler;
//                                ^
//              doesn't work, types are incompatible

推荐答案

这不是一个真正公平的答案,因为自从您发布问题以来我已经扩展了 Autofac... :)

根据 Daniel 的回答,您需要将 in 修饰符添加到 ICommandHandlerTCommand 参数中:

As per Daniel's answer, you'll need to add the in modifier to the TCommand parameter of ICommandHandler:

interface ICommandHandler<in TCommand>
{
    void Handle(TCommand command);
}

Autofac 2.5.2 现在包含一个 IRegistrationSource 以启用逆变Resolve() 操作:

Autofac 2.5.2 now includes an IRegistrationSource to enable contravariant Resolve() operations:

using Autofac.Features.Variance;

var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());

注册此源后,将在考虑变体实现的情况下查找由具有单个 in 参数的通用接口表示的服务:

With this source registered, services represented by a generic interface with a single in parameter will be looked up taking variant implementations into account:

builder.RegisterType<FooCommandHandler>()
   .As<ICommandHandler<FooCommand>>();

var container = builder.Build();
container.Resolve<ICommandHandler<FooCommand>>();
container.Resolve<ICommandHandler<SpecialFooCommand>>();

Resolve() 的两次调用都将成功检索 FooCommandHandler.

Both calls to Resolve() will successfully retrieve the FooCommandHandler.

如果您无法升级到最新的 Autofac 软件包,请从 http://code.google.com/p/autofac/source/browse/src/Source/Autofac/Features/Variance/ContravariantRegistrationSource.cs - 它应该针对任何最近的 Autofac 版本进行编译.

If you can't upgrade to the latest Autofac package, grab the ContravariantRegistrationSource from http://code.google.com/p/autofac/source/browse/src/Source/Autofac/Features/Variance/ContravariantRegistrationSource.cs - it should compile against any recent Autofac build.

这篇关于自定义 Autofac 的组件分辨率/通用协方差/逆变问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆