自定义 Autofac 的组件分辨率/通用协方差/逆变问题 [英] Customizing Autofac's component resolution / Issue with generic co-/contravariance
问题描述
首先,抱歉问题标题含糊不清.我想不出更精确的了.
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
修饰符添加到 ICommandHandler
的 TCommand
参数中:
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屋!