Unity:使用一个接口的两个实现来解析类

时间:2016-02-14 19:59:52

标签: c# dependency-injection inversion-of-control unity-container

我的结构如下:

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实例(第一个param应该获得ServiceA,第二个 - ServiceB)或者它会导致一种模糊性吗?

1 个答案:

答案 0 :(得分:3)

如果您尝试运行已发布的代码,您将收到一个例外:

  

当前类型IBaseService是一个接口   并且无法构建。你错过了类型映射吗?

这是因为在IBaseService的容器中没有注册默认注册(默认值没有名称)。注册的命名接口映射只有两个。

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

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

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

然后像这样注册容器:

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

这会将IEnumerable<IBaseService>映射到IBaseService的数组,因为当Unity看到一个数组时,它会注入所有命名的注册并将其注入对象。您也可以在没有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).