问题描述
首先,比较遗憾的是模糊的问题称号。我不能拿出一个更精确的。
由于这些类型的:
{TCommand:ICommand的}
«接口»«接口»/
+ ----------- + + ---------------------- / ---- +
| ICommand的| | ICommandHandler< TCommand> |
+ ----------- + + + ---------------------------
^ |手柄(命令:TCommand)|
| + --------------------------- +
| ^
| |
+ ------------ + + + -------------------
| FooCommand | | FooCommandHandler |
+ ------------ + + + -------------------
^
|
+ ------------------- +
| SpecialFooCommand |
+ ------------------- +
我想编写一个接受的任何命令,并将其发送到合适的 ICommandHandler℃的方法
。我认为,使用DI容器(Autofac)可能会极大地简化了从命令的类型映射到命令处理程序:调度
;>
无效调度< ; TCommand>(TCommand命令),其中TCommand:ICommand的
{
变种处理程序= autofacContainer.Resolve< ICommandHandler< TCommand>>();
handler.Handle(命令);
}
让我们说的DI容器知道的所有上述类型。现在,我打电话:
调度(新SpecialFooCommand(...));
在现实中,这将导致Autofac投掷 ComponentNotRegisteredException
,因为没有 ICommandHandler< SpecialFooCommand方式>
可用
然而理想,我仍然希望有一个 SpecialFooCommand
来通过提供的最接近的匹配命令处理程序,即处理。由 FooCommandHandler
在上面的例子。
能否Autofac为此目的进行定制,或许还有一个自定义的注册源?
PS:据我了解,有可能是合作的根本问题/逆变获得的方式(如下面的例子),而唯一的解决方案可能是一个根本不使用泛型...但我想如果可能的话坚持泛型类型。
ICommandHandler< FooCommand> fooHandler =新FooCommandHandler(...);
ICommandHandler<&的ICommand GT;处理器= fooHandler;
// ^
//不起作用,类型不兼容
的不是一个真正的公平的答案,因为我已经扩展Autofac因为你张贴的问题...:)的
根据丹尼尔的回答,你需要在修改添加来的
TCommand
参数 ICommandHandler
:
接口ICommandHandler<在TCommand>
{
无效手柄(TCommand命令);
}
Autofac 2.5.2现在包括一个 IRegistrationSource
,使逆变解析()
操作:
使用Autofac.Features.Variance;
变种建设者=新ContainerBuilder();
builder.RegisterSource(新ContravariantRegistrationSource());
通过这个源注册,通过通用接口与一个在代表服务
参数将被抬起头来考虑变体实施到:
builder.RegisterType< FooCommandHandler>()
。至于< ICommandHandler< FooCommand>>();
VAR容器= builder.Build();
container.Resolve< ICommandHandler< FooCommand>>();
container.Resolve< ICommandHandler< SpecialFooCommand>>();
到同时解决电话()
成功地将检索 FooCommandHandler
。
如果您不能升级到最新的Autofac包,抢 ContravariantRegistrationSource
://code.google.com/p/autofac/source/browse/src/Source/Autofac/Features/Variance/ContravariantRegistrationSource.cs - 它应该编译针对任何近期Autofac建立
First, sorry for the vague question title. I couldn't come up with a more precise one.
Given these types:
{ TCommand : ICommand }
«interface» «interface» /
+-----------+ +----------------------/----+
| ICommand | | ICommandHandler<TCommand> |
+-----------+ +---------------------------+
^ | Handle(command: TCommand) |
| +---------------------------+
| ^
| |
+------------+ +-------------------+
| FooCommand | | FooCommandHandler |
+------------+ +-------------------+
^
|
+-------------------+
| SpecialFooCommand |
+-------------------+
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);
}
Let's say the DI container knows about all the types shown above. Now I'm calling:
Dispatch(new SpecialFooCommand(…));
In reality, this will result in Autofac throwing a ComponentNotRegisteredException
, since there is no ICommandHandler<SpecialFooCommand>
available.
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.
Can Autofac be customized towards that end, perhaps with a custom registration source?
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
Not really a fair answer, as I've extended Autofac since you posted the question... :)
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 now includes an IRegistrationSource
to enable contravariant Resolve()
operations:
using Autofac.Features.Variance;
var builder = new ContainerBuilder();
builder.RegisterSource(new ContravariantRegistrationSource());
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>>();
Both calls to Resolve()
will successfully retrieve the FooCommandHandler
.
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的组件解决/问题与通用的合作/逆变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!