Unity:解析 class 同一个接口的两种实现

Unity: resolve class with two implementations of one interface

我的结构如下:

static void Main(string[] args) {
    var container = new UnityContainer();
    container.RegisterType<IBaseService, ServiceA>("a");
    container.RegisterType<IBaseService, ServiceB>("b");
    container.RegisterType<IWorker, Worker>();

    var runner = container.Resolve<Runner>();
    /* ... */
}

public class Runner {
    public Runner(IWorker worker) {}

}

public class Worker : IWorker {
    public Worker(IBaseService a, IBaseService b) { /* ... */ }
}

public class ServiceA : IBaseService {}
public class ServiceB : IBaseService {}

我知道我可以为 ServiceA 设置额外的接口,如 IServiceA(与 ServiceB - IServiceB 相同),并将它们注册到容器中。 但是是否可以使用具有相同合同 IBaseService 的两个服务来解析 Worker 的实例(第一个参数应该得到 ServiceA,第二个参数应该是 ServiceB),否则会导致某种歧义?

如果您尝试 运行 发布的代码,您将收到一个异常:

The current type, IBaseService, is an interface and cannot be constructed. Are you missing a type mapping?

这是因为容器中没有为IBaseService注册默认注册(默认意思是没有名字)。仅注册了两个命名接口映射。

处理这种情况的方法很少。正如@Haukinger 所提到的,一种方法是将所有 IBaseService 实现注入到对象中。

所以你可以这样声明你的class:

public class Worker2 : IWorker
{
    public Worker2(IEnumerable<IBaseService> baseServices)
    {
        /* ... */
    }
}

然后像这样注册容器:

container.RegisterType(typeof(IEnumerable<IBaseService>), typeof(IBaseService[]));

这将一个 IEnumerable<IBaseService> 映射到一个 IBaseService 的数组,这是有效的,因为当 Unity 看到一个数组时,它将注入使用所有 named 注册并注入那些进入对象。您也可以在没有 IEnumerable 映射的情况下直接使用数组。

这种方法可能很有用,但您通常想知道注入的是哪种类型(例如,工厂必须根据其他一些标准在运行时间选择正确的实现)。

另一种方法是配置 Unity 以将正确的命名注册注入 Worker:

container.RegisterType<IWorker, Worker>(
    new InjectionConstructor(
        new ResolvedParameter<IBaseService>("a"), 
        new ResolvedParameter<IBaseService>("b")));

在上面的例子中,我们告诉 Unity 使用什么构造函数以及注入哪个实现。

另一种方法是使用 InjectionFactory:

container.RegisterType<IWorker, Worker>(
    new InjectionFactory(
        c => new Worker(c.Resolve<IBaseService>("a"), c.Resolve<IBaseService>("b"))
        ));

使用 InjectionFactory 的一个好处是,因为我们正在更新 Worker 对象,所以我们可以对构造函数进行编译器检查。例如如果更改 Worker 构造函数以添加新参数,则会出现编译错误。相反,InjectionConstructor 方法可以编译但会生成 运行 时间错误 System.InvalidOperationException: The type Worker does not have a constructor that takes the parameters (IBaseService, IBaseService).