Microsoft DI中不同类构造参数为同类型时如何从配置注入不同值
实现方案
针对微软通用宿主DI下同类型不同实例定向注入的需求,有以下几种常用的落地方案:
方案1:使用实现工厂重载注册(兼容所有.NET版本)
这是最直观无需额外依赖的方案,直接调用服务注册的工厂重载,在构造目标服务时手动传入对应参数实例:
// 你已有的参数读取逻辑 SomeKindOfParameter parameterForTypeX = config.GetSection("ParamForX").Get<SomeKindOfParameter>(); SomeKindOfParameter parameterForTypeY = config.GetSection("ParamForY").Get<SomeKindOfParameter>(); // 不全局注册SomeKindOfParameter,单独为每个服务指定注入的参数 services.AddTransient<IX, X>(serviceProvider => { // 如果X还有其他依赖,直接从serviceProvider获取即可,比如: // var logger = serviceProvider.GetRequiredService<ILogger<X>>(); return new X(parameterForTypeX); }); services.AddTransient<IY, Y>(serviceProvider => new Y(parameterForTypeY));
该方案的优势是不需要修改现有X、Y类的代码,适配所有.NET版本,缺点是如果目标服务构造函数参数较多,工厂委托的编写会比较繁琐。
方案2:使用键控服务(.NET 8+ 官方原生支持)
.NET 8开始DI原生支持键控服务特性,可以给同类型的不同实例标记唯一键,在构造函数中指定要注入的键即可:
第一步:注册带键的参数实例
services.AddKeyedSingleton<SomeKindOfParameter>("ParamForX", parameterForTypeX); services.AddKeyedSingleton<SomeKindOfParameter>("ParamForY", parameterForTypeY); // 正常注册X、Y服务 services.AddTransient<IX, X>(); services.AddTransient<IY, Y>();
第二步:给构造函数参数加特性标记对应键
public class X : IX { public X([FromKeyedServices("ParamForX")] SomeKindOfParameter param) { // 原有逻辑不变 } } public class Y : IY { public Y([FromKeyedServices("ParamForY")] SomeKindOfParameter param) { // 原有逻辑不变 } }
该方案是官方推荐的标准化实现,代码简洁易维护,仅要求运行环境为.NET 8及以上版本。
方案3:使用包装类/标记接口(兼容低版本.NET,不想写工厂时用)
如果需要兼容.NET 8之前的版本,又不想写冗长的工厂委托,可以给两个参数做简单的类型区分:
第一步:定义空的标记接口/派生类
// 空标记接口,仅做类型区分用 public interface IParamForX {} public interface IParamForY {} // 派生参数类继承标记接口 public class ParamForX : SomeKindOfParameter, IParamForX {} public class ParamForY : SomeKindOfParameter, IParamForY {}
第二步:读取配置并注册对应类型
ParamForX parameterForTypeX = config.GetSection("ParamForX").Get<ParamForX>(); ParamForY parameterForTypeY = config.GetSection("ParamForY").Get<ParamForY>(); services.AddSingleton<IParamForX>(parameterForTypeX); services.AddSingleton<IParamForY>(parameterForTypeY); services.AddTransient<IX, X>(); services.AddTransient<IY, Y>();
第三步:修改构造函数接收对应标记接口类型
public class X : IX { public X(IParamForX param) { // 可直接转为SomeKindOfParameter使用 var typedParam = (SomeKindOfParameter)param; } }
该方案不需要写工厂逻辑,适配所有.NET版本,仅需要新增几个简单的标记类型即可。
内容的提问来源于stack exchange,提问作者Bob Vesterman
相关产品推荐
相关产品推荐

