Simple Injector不允许注册同一接口的多个具体实现
这句话不正确。实际上有多种方法可以做到这一点。我认为最常见的三种方法是:
-
使用条件注册
-
注册类型集合
-
手动创建
InstanceProducer
实例。
特别是选项1和3似乎最适合您的情况,所以让我们从选项3开始:创建
InstanceProducers
:
// Create two providers for IProvider according to the required lifestyle.
var bing = Lifestyle.Singleton.CreateProducer<IProvider, BingProvider>(container);
var google = Lifestyle.Singleton.CreateProducer<IProvider, GoogleProvider>(container);
container.RegisterSingleton<ProviderFactory>(new ProviderFactory(provider => {
switch (provider) {
case "Bing": return bing.GetInstance();
case "Google": return google.GetInstance();
default: throw new ArgumentOutOfRangeException();
}
}));
这里我们创建两个
InstanceProducer(实例生产者)
实例,每个实例一个
IProvider
。这里的重要部分是为
I提供商
抽象,因为这允许
I提供商
待应用。
或者,您可以选择移动
switch
-
case
声明中的
ProviderFactory
并为其配备两名单独的代表;每个提供商一个。例如:
public ProviderFactory(Func<IProvider> bingProvider, Func<IProvider> googleProvider) { .. }
public IProvider Get(string provider) {
switch (provider) {
case "Bing": return bingProvider();
case "Google": return googleProvider();
default: throw new ArgumentOutOfRangeException();
}
}
注册看起来与前面的非常相似:
var bing = Lifestyle.Singleton.CreateProducer<IProvider, BingProvider>(container);
var google = Lifestyle.Singleton.CreateProducer<IProvider, GoogleProvider>(container);
container.RegisterSingleton<ProviderFactory>(new ProviderFactory(
bingProvider: bing.GetInstance,
googleProvider: google.GetInstance));
而不是注射
Func<T>
代表进入工厂,根据您的需要,它可能适合您注入
I提供商
直接地这意味着您的构造函数将如下所示:
public ProviderFactory(IProvider bing, IProvider google) { ... }
现在您可以在上使用条件注册
I提供商
要消除构造函数参数的歧义,请执行以下操作:
container.RegisterSingleton<ProviderFactory>();
container.RegisterConditional<IProvider, BingProvider>(
c => c.Consumer.Target.Name == "bing");
container.RegisterConditional<IProvider, GoogleProvider>(
c => c.Consumer.Target.Name == "google");
这样做的优点是您不会延迟构建对象图;当工厂的消费者得到解决时,将直接注入所有提供者。
或者,您可能还想尝试一种设计,用调度程序抽象替换工厂。对于消费者来说,工厂抽象通常不是最简单的解决方案,因为它们现在必须处理工厂类型和返回的服务抽象。另一方面,调度程序或处理器为消费者提供了一个抽象。这使得消费者(及其单元测试)通常更简单。
这样的调度员看起来很像
I提供商
接口本身,但添加了
string provider
参数设置为其实例方法。例如:
interface IProviderDispatcher {
void DoSomething(string provider, ProviderData data);
}
其中,调度程序的实现可能如下所示:
public ProviderDispatcher(IProvider bing, IProvider google) { .. }
public void DoSomething(string provider, ProviderData data) {
this.Get(provider).DoSomething(data);
}
private IProvider Get(string provider) {
switch (provider) {
case "Bing": return this.bing;
case "Google": return this.google;
default: throw new ArgumentOutOfRangeException();
}
}
调度器的解决方案可以与工厂的相同,但现在我们对消费者隐藏了额外的步骤。
如果我们能移除
IProviderDispatcher
完全抽象,但只有在
字符串提供程序
运行时数据是请求期间可用的上下文数据。在这种情况下,我们可以执行以下操作:
interface IProviderContext {
string CurrentProvider { get; }
}
我们可以在
I提供商
:
class ProviderDispatcherProxy : IProvider {
public ProviderDispatcherProxy(Func<IProvider> bingProvider,
Func<IProvider> googleProvider,
IProviderContext providerContext) { ... }
void IProvider.DoSomething(ProviderData data) {
// Dispatch to the correct provider
this.GetCurrentProvider.DoSomething(data);
}
private IProvider GetCurrentProvider() =>
switch (this.providerContext.CurrentProvider) {
case "Bing": return this.bingProvider();
case "Google": return this.googleProvider();
default: throw new ArgumentOutOfRangeException();
}
};
}
class AspNetProviderContext : IProviderContext {
public CurrentProvider => HttpContext.Current.Request.QueryString["provider"];
}
同样,在内部它仍然很像以前,但现在,因为提供者值是我们可以从可用的环境上下文(
HttpContext.Current
),我们将能够让消费者与
I提供商
直接地您可以按如下方式注册:
container.RegisterSingleton<IProviderContext>(new AspNetProviderContext());
var bing = Lifestyle.Singleton.CreateProducer<IProvider, BingProvider>(container);
var google = Lifestyle.Singleton.CreateProducer<IProvider, GoogleProvider>(container);
container.RegisterSingleton<IProvider>(new ProviderDispatcherProxy(
bingProvider: bing.GetInstance,
googleProvider: google.GetInstance));
现在您可以简单地注入
I提供商
你的消费者和调度将自动在后台为你发生。