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).
我的结构如下:
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).