自定义Autofac的组件解决/问题与通用的合作/逆变 [英] Customizing Autofac's component resolution / Issue with generic co-/contravariance

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

问题描述

首先,比较遗憾的是模糊的问题称号。我不能拿出一个更精确的。



由于这些类型的:

  {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的组件解决/问题与通用的合作/逆变的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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