.NET依赖注入中动态选择实现的最优方案咨询
.NET依赖注入中条件选择接口实现的最优方案探讨
需求背景
需要在.NET依赖注入容器中注册IExampleService接口的两个不同实现(ExampleAService和ExampleBService),并能在特定时机根据条件动态选择实例化对应的实现类。已尝试工厂模式和工厂委托,希望找到更优的实现方式。
示例代码定义
IExampleService 接口与枚举
internal interface IExampleService { public ExampleMode Mode { get; } void Show(); } internal enum ExampleMode { ExampleA, ExampleB }
ExampleAService 实现
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"); }
ExampleBService 实现
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<IExampleService>
通过DI容器注入所有实现的集合,由容器管理生命周期,但会导致所有实现类被提前实例化,带来不必要的性能开销:
internal class ExampleServiceFactory : IExampleServiceFactory { private readonly IEnumerable<IExampleService> exampleServices; public ExampleServiceFactory(IEnumerable<IExampleService> exampleServices) { this.exampleServices = exampleServices; } public IExampleService GetExampleService(ExampleMode mode) { // 使用DI注入的服务集合 return exampleServices.FirstOrDefault(x => x.Mode == mode); } }
方式二:手动实例化服务
手动创建所需服务实例,但需要自行管理对象释放,且无法利用DI容器的生命周期管理:
internal class ExampleServiceFactory : IExampleServiceFactory { public IExampleService GetExampleService(ExampleMode mode) { // 手动实例化服务 return mode switch { ExampleMode.ExampleA => new ExampleAService(), ExampleMode.ExampleB => new ExampleBService(), _ => throw new NotSupportedException(), }; } }
需避免的反模式:注入IServiceProvider
曾考虑在工厂中注入IServiceProvider直接解析服务,但这属于服务定位器反模式,会混合控制反转策略,不推荐:
另一种需要避免的服务定位器变体是注入在运行时解析依赖的工厂。这两种做法都会混合控制反转策略。
当前使用的实现方式:工厂委托+键注册
目前通过键注册服务,结合工厂委托在解析时根据条件选择实例,同时保留DI容器的生命周期管理,最终注入包装类供页面、控制器等使用:
builder.Services.AddKeyedSingleton<IExampleService,ExampleAService>("ExampleA"); builder.Services.AddKeyedTransient<IExampleService,ExampleBService>("ExampleB"); builder.Services.AddTransient<IExampleServiceFactory,ExampleServiceFactory>(); builder.Services.AddTransient<ConfigurationProvider>(); //var exampleMode = ConfigurationManager.AppSettings["Device"]; builder.Services.AddTransient<IGenericExampleService, GenericExampleService>((ctx) => { var configProvider = ctx.GetRequiredService<ConfigurationProvider>(); var exampleMode = configProvider.GetExampleMode(); var service = exampleMode switch { ExampleMode.ExampleA => ctx.GetRequiredKeyedService<IExampleService>("ExampleA"), ExampleMode.ExampleB => ctx.GetRequiredKeyedService<IExampleService>("ExampleB"), _ => throw new InvalidOperationException() }; return new GenericExampleService(service); });
内容的提问来源于stack exchange,提问作者O.Emad
相关产品推荐
相关产品推荐

