我的结构如下:
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)或者它会导致一种模糊性吗?
答案 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).