ASP.NET Core DI如何仅为特定类型的依赖树注入指定IBar实现
ASP.NET DI 同接口多实现定向注入解决方案
ASP.NET Core 自带DI没有原生的子容器/派生容器正式支持,但有三种成熟的落地方案可以实现需求,复杂度从低到高对应不同的项目场景:
方案1:键控服务(.NET 8+ 原生支持)
这是目前最简单的原生实现方式,无额外依赖:
- 注册阶段给指定实现绑定唯一标识:
// 给不同IBar实现绑定对应消费类类型作为唯一键 builder.Services.AddKeyedSingleton<IBar, FooBar>(typeof(Foo)); builder.Services.AddKeyedSingleton<IBar, DefaultBar>(typeof(Baz)); builder.Services.AddKeyedSingleton<IBar, DefaultBar>(typeof(Qux)); // 可同时注册全局默认IBar实现,无键注册优先级低于键控注册 builder.Services.AddSingleton<IBar, DefaultBar>();
- 消费类构造函数通过
[FromKeyedServices]特性指定要注入的实现:
public class Foo { public Foo([FromKeyedServices(typeof(Foo))] IBar bar) { // 此处拿到的就是FooBar实例 } }
该方案完全原生无额外逻辑,如果需要传递性注入,只要Foo的子依赖也用相同的键指定
IBar即可。
方案2:工厂注册指定实现(全版本兼容)
如果项目使用.NET 7及以下版本,不支持键控服务,可以用DI提供的工厂注册重载实现,无需自行编写反射逻辑:
// 先注册所有IBar实现 builder.Services.AddSingleton<FooBar>(); builder.Services.AddSingleton<DefaultBar>(); // 注册消费类时显式指定IBar实现 builder.Services.AddSingleton<Foo>(sp => ActivatorUtilities.CreateInstance<Foo>(sp, sp.GetRequiredService<FooBar>()) ); builder.Services.AddSingleton<Baz>(sp => ActivatorUtilities.CreateInstance<Baz>(sp, sp.GetRequiredService<DefaultBar>()) ); builder.Services.AddSingleton<Qux>(sp => ActivatorUtilities.CreateInstance<Qux>(sp, sp.GetRequiredService<DefaultBar>()) );
ActivatorUtilities.CreateInstance会自动解析消费类的其他构造参数,仅IBar类型的参数会使用你传入的实例填充,不需要自行实现构造函数扫描、依赖解析逻辑。
方案3:作用域模拟子容器实现(满足传递性注入强制要求)
如果必须实现Foo的所有子依赖自动拿到FooBar实例、不需要额外加特性的要求,可以用作用域模拟子容器效果:
// 注册全局默认IBar builder.Services.AddSingleton<IBar, DefaultBar>(); // 单独注册FooBar实现 builder.Services.AddSingleton<FooBar>(); // 解析Foo时创建独立服务提供器替换IBar实现 builder.Services.AddSingleton<Foo>(sp => { // 复制全局所有服务注册 var services = new ServiceCollection(); foreach (var service in sp.GetRequiredService<IEnumerable<ServiceDescriptor>>()) { services.Add(service); } // 替换当前服务集合的IBar注册为FooBar services.AddSingleton<IBar>(sp.GetRequiredService<FooBar>()); // 构建独立服务提供器解析Foo var childProvider = services.BuildServiceProvider(); return childProvider.GetRequiredService<Foo>(); });
该方案有一定性能损耗,仅适合不频繁解析的单例服务场景,瞬态、作用域服务不推荐使用。
优先推荐方案1,只要项目运行在.NET 8及以上版本,是性能最优、维护成本最低的实现。
内容的提问来源于stack exchange,提问作者Bruno Brant
相关产品推荐
相关产品推荐

