对于一个接口,我有两个不同的实现,我需要在依赖注入中注册它们,并且我需要有条件地检查在给定时刻要实例化哪个实现。 我已经使用了工厂模式和工厂委托,我只是想知道是否有更好的方法来实现这一点。
我会使用简单的对象来保持简单
I示例服务
internal interface IExampleService
{
public ExampleMode Mode { get; }
void Show();
}
internal enum ExampleMode
{
ExampleA,
ExampleB
}
示例A服务
public ExampleMode Mode => ExampleMode.ExampleA;
public ExampleAService()
{
Console.WriteLine("Creating Example A");
}
public void Dispose()
{
Console.WriteLine("Disposing Example A");
}
public void Show()
{
Console.WriteLine("Example A Show");
}
示例B服务
public ExampleMode Mode => ExampleMode.ExampleB;
public ExampleBService()
{
Console.WriteLine("Creating Example B");
}
public void Dispose()
{
Console.WriteLine("Disposing Example B");
}
public void Show()
{
Console.WriteLine("Example B Show");
}
实现依赖注入的条件实现选择的适当方法是什么。
我使用了两种不同的工厂模式实现,第一个采用 Ienumerable
示例服务工厂
internal class ExampleServiceFactory : IExampleServiceFactory
{
private readonly IEnumerable<IExampleService> exampleServices;
public ExampleServiceFactory(IEnumerable<IExampleService> exampleServices)
{
this.exampleServices = exampleServices;
}
public IExampleService GetExampleService(ExampleMode mode)
{
//Using DI to inject Ienumerable of services
return exampleServices.FirstOrDefault(x => x.Mode == mode);
//Manually instantiate Services
//return mode switch
//{
// ExampleMode.ExampleA => new ExampleAService(),
// ExampleMode.ExampleB => new ExampleBService(),
// _ => throw new NotSupportedException(),
//};
}
}
向工厂注入 IServiceProvider 对我来说非常有意义,但这似乎是一种反模式
要避免的另一个服务定位器变体是注入一个在运行时解析依赖关系的工厂。这两种做法都混合了控制反转策略。
目前我正在使用工厂委托有条件地实例化所需的实现并将其传递给一个包装器,我可以自由地将其注入到其他页面、控制器等。
程序.cs
builder.Services.AddKeyedSingleton<IExampleService,ExampleAService>("ExampleA");
builder.Services.AddKeyedTransient<IExampleService,ExampleBService>("ExampleB");
builder.Services.AddTransient<IExampleServiceFactory,ExampleServiceFactory>();
var exampleMode = ConfigurationManager.AppSettings["Device"];
builder.Services.AddTransient<IGenericExampleService, GenericExampleService>((ctx) =>
{
var service = Enum.Parse(typeof(ExampleMode), exampleMode) switch
{
ExampleMode.ExampleA => ctx.GetRequiredKeyedService<IExampleService>("ExampleA"),
ExampleMode.ExampleB => ctx.GetRequiredKeyedService<IExampleService>("ExampleB"),
_ => throw new InvalidOperationException()
};
return new GenericExampleService(service);
});
从上一个示例中可以清楚地看出,无需动态切换实现,因为您正在从配置加载
ExampleMode
,并且只有应用程序重新启动才能更改该值。
因此,我提出这个更简单的解决方案:
var mode =
Enum.Parse(typeof(ExampleMode), ConfigurationManager.AppSettings["Device"]);
if (mode == ExampleMode.ExampleA)
{
builder.Services.AddTransient<IExampleService, ExampleAService>();
}
else
{
builder.Services.AddTransient<IExampleService, ExampleBService>();
}
因为在运行时总是只需要一个
IExampleService
实现,您只需注册该实现即可,让消费者直接依赖 IExampleService
。不需要工厂,不需要注射IEnumerable<IExampleService>
.