.NET 10 ServiceCollection中为不同控制器配置不同IDataWriter实例
在.NET 10中为不同控制器配置不同的依赖链(共享中间服务)
可以通过.NET 10支持的**键控服务(Keyed Services)**来实现这个需求,无需在底层服务中添加额外参数,完全通过依赖注入配置来绑定不同的依赖链。以下是具体实现步骤:
1. 键控服务方案(推荐)
调整服务注册逻辑,为不同依赖链绑定专属标识,让DI容器自动匹配对应实例:
// 注册带键的IDataWriter实现,用"file"/"database"区分不同写入器 services.AddKeyedSingleton<IDataWriter, FileDataWriter>("file"); services.AddKeyedSingleton<IDataWriter, DatabaseDataWriter>("database"); // 注册带键的IAnonymiser,绑定对应键的IDataWriter services.AddKeyedScoped<IAnonymiser, NameAnonymiser>("file", (sp, key) => new NameAnonymiser(sp.GetRequiredKeyedService<IDataWriter>(key))); services.AddKeyedScoped<IAnonymiser, NameAnonymiser>("database", (sp, key) => new NameAnonymiser(sp.GetRequiredKeyedService<IDataWriter>(key))); // 注册带键的IFormatter,绑定对应键的IAnonymiser services.AddKeyedScoped<IFormatter, AddressFormatter>("file", (sp, key) => new AddressFormatter(sp.GetRequiredKeyedService<IAnonymiser>(key))); services.AddKeyedScoped<IFormatter, AddressFormatter>("database", (sp, key) => new AddressFormatter(sp.GetRequiredKeyedService<IAnonymiser>(key))); // 显式绑定控制器与对应键的IFormatter services.AddScoped<CustomerFileController>(sp => new CustomerFileController(sp.GetRequiredKeyedService<IFormatter>("file"))); services.AddScoped<CustomerDatabaseController>(sp => new CustomerDatabaseController(sp.GetRequiredKeyedService<IFormatter>("database")));
方案说明
- 键控服务:通过标识键区分不同的依赖分支,确保中间服务能绑定到对应链的
IDataWriter。 - Scoped生命周期:将
IAnonymiser和IFormatter设为Scoped,保证每个请求创建新实例,避免单例服务无法动态切换依赖的问题。 - 无侵入修改:底层服务(
NameAnonymiser/AddressFormatter)无需任何改动,所有逻辑在DI配置层完成。
2. 替代方案:工厂模式
如果偏好不使用键控服务,可通过工厂模式实现依赖分支的切换:
- 定义工厂接口与实现:
public interface IFormatterFactory { IFormatter GetFormatter(Type controllerType); } public class FormatterFactory : IFormatterFactory { private readonly IServiceProvider _serviceProvider; public FormatterFactory(IServiceProvider serviceProvider) { _serviceProvider = serviceProvider; } public IFormatter GetFormatter(Type controllerType) { return controllerType switch { Type t when t == typeof(CustomerFileController) => new AddressFormatter(new NameAnonymiser(_serviceProvider.GetRequiredService<FileDataWriter>())), Type t when t == typeof(CustomerDatabaseController) => new AddressFormatter(new NameAnonymiser(_serviceProvider.GetRequiredService<DatabaseDataWriter>())), _ => throw new ArgumentOutOfRangeException(nameof(controllerType)) }; } }
- 注册工厂与控制器:
services.AddSingleton<FileDataWriter>(); services.AddSingleton<DatabaseDataWriter>(); services.AddSingleton<IFormatterFactory, FormatterFactory>(); services.AddScoped<CustomerFileController>(sp => new CustomerFileController(sp.GetRequiredService<IFormatterFactory>().GetFormatter(typeof(CustomerFileController)))); services.AddScoped<CustomerDatabaseController>(sp => new CustomerDatabaseController(sp.GetRequiredService<IFormatterFactory>().GetFormatter(typeof(CustomerDatabaseController))));
关键注意事项
- 禁止将
IAnonymiser和IFormatter注册为Singleton:单例服务只会初始化一次,无法动态切换依赖的IDataWriter实例。 - 完全符合需求:无需向底层服务传递任何标识参数,依赖切换逻辑完全由DI容器或工厂处理。
内容的提问来源于stack exchange,提问作者Sigurd Garshol
相关产品推荐
相关产品推荐

