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

本文关键字:两个 实现 一个 Unity 接口 | 更新日期: 2023-09-27 18:34:31

我的结构如下:

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(,或者这会导致某种歧义?

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

如果您尝试按照发布的方式运行代码,您将收到异常:

当前类型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 配置为将正确的命名注册注入工作线程:

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).