如何在依赖注入中动态选择要使用的具体类
动态配置DI服务实现的方案
一、根据配置动态注册服务实现
你可以在构建ServiceCollection阶段直接读取配置,根据配置值选择对应的IOutputMaker实现进行注册,无需提前固定死实现类。
方式1:分支判断注册
直接读取配置中的输出类型,通过分支逻辑注册对应实现:
// 从配置中读取输出类型 var outputType = Program.configuration["OutputType"]; var services = new ServiceCollection() .AddSingleton<ISomething, Something>() .AddSingleton<IConfiguration>(Program.configuration); // 根据配置动态添加IOutputMaker实现 switch (outputType) { case "XML": services.AddSingleton<IOutputMaker, XMLOutputMaker>(); break; case "XLSX": services.AddSingleton<IOutputMaker, ExcelOutputMaker>(); break; default: // 可抛出异常或注册默认实现 throw new InvalidOperationException($"不支持的输出类型:{outputType}"); } var serviceProvider = services.BuildServiceProvider();
方式2:反射动态注册(扩展性更强)
如果需要新增输出类型时无需修改DI注册代码,可以在配置中指定实现类的全限定名,通过反射获取类型并注册:
var implTypeName = Program.configuration["OutputMakerImpl"]; var implType = Type.GetType(implTypeName); if (implType == null || !typeof(IOutputMaker).IsAssignableFrom(implType)) { throw new InvalidOperationException("配置的输出类型无效"); } var services = new ServiceCollection() .AddSingleton<ISomething, Something>() .AddSingleton<IConfiguration>(Program.configuration) .AddSingleton(typeof(IOutputMaker), implType); var serviceProvider = services.BuildServiceProvider();
二、关于多次调用BuildServiceProvider
不建议多次调用BuildServiceProvider。每次调用都会创建一个全新的服务容器实例,容器内的单例服务会被重复实例化,既违背单例设计意图,也会造成资源浪费。
如果需要支持运行时动态切换输出类型(比如配置更新后无需重启),可以采用以下替代方案:
方案1:使用工厂模式
注册一个工厂类,由工厂根据当前配置创建对应的IOutputMaker实例:
// 定义工厂接口 public interface IOutputMakerFactory { IOutputMaker GetOutputMaker(); } // 实现工厂类 public class OutputMakerFactory : IOutputMakerFactory { private readonly IConfiguration _config; private readonly IServiceProvider _serviceProvider; public OutputMakerFactory(IConfiguration config, IServiceProvider serviceProvider) { _config = config; _serviceProvider = serviceProvider; } public IOutputMaker GetOutputMaker() { var outputType = _config["OutputType"]; return outputType switch { "XML" => _serviceProvider.GetRequiredService<XMLOutputMaker>(), "XLSX" => _serviceProvider.GetRequiredService<ExcelOutputMaker>(), _ => throw new InvalidOperationException($"不支持的输出类型:{outputType}") }; } }
注册时需要把所有可能的IOutputMaker实现都注册到容器:
var services = new ServiceCollection() .AddSingleton<ISomething, Something>() .AddSingleton<IConfiguration>(Program.configuration) .AddSingleton<XMLOutputMaker>() .AddSingleton<ExcelOutputMaker>() .AddSingleton<IOutputMakerFactory, OutputMakerFactory>(); var serviceProvider = services.BuildServiceProvider();
使用时通过工厂获取实例:
var factory = serviceProvider.GetRequiredService<IOutputMakerFactory>(); var outputMaker = factory.GetOutputMaker();
方案2:使用.NET 8+的键控服务(Keyed Service)
如果使用.NET 8及以上版本,可以利用键控服务给不同的IOutputMaker实现标记键,然后根据配置值获取对应键的服务:
var services = new ServiceCollection() .AddSingleton<ISomething, Something>() .AddSingleton<IConfiguration>(Program.configuration) // 给不同实现标记对应的键 .AddKeyedSingleton<IOutputMaker, XMLOutputMaker>("XML") .AddKeyedSingleton<IOutputMaker, ExcelOutputMaker>("XLSX"); var serviceProvider = services.BuildServiceProvider(); // 使用时根据配置键获取服务 var outputType = Program.configuration["OutputType"]; var outputMaker = serviceProvider.GetRequiredKeyedService<IOutputMaker>(outputType);
内容的提问来源于stack exchange,提问作者UnskilledCoder
相关产品推荐
相关产品推荐

