泛型接口派生类DI注入问题:IModelProcessor实现无法注入工厂
问题原因
你注册的IModelProcessor<ConcreteOne>等具体泛型服务,与工厂依赖的IModelProcessor<MyAbstractClass>在.NET泛型系统中是完全独立的类型——默认情况下,泛型接口不支持隐式类型转换,因此DI容器无法将前者识别为后者的实现,导致注入时找不到匹配的服务,最终集合为null。
解决方案
方案1:显式注册为基泛型接口
在注册每个处理器时,额外将其注册为IModelProcessor<MyAbstractClass>类型,让DI容器将这些实例纳入目标集合:
// 注册具体泛型接口 builder.Services.AddScoped<IModelProcessor<ConcreteOne>, ConcreteOneProcessor>(); // 同时注册为基泛型接口 builder.Services.AddScoped<IModelProcessor<MyAbstractClass>>(sp => sp.GetRequiredService<IModelProcessor<ConcreteOne>>()); builder.Services.AddScoped<IModelProcessor<ConcreteTwo>, ConcreteTwoProcessor>(); builder.Services.AddScoped<IModelProcessor<MyAbstractClass>>(sp => sp.GetRequiredService<IModelProcessor<ConcreteTwo>>()); builder.Services.AddScoped<IModelProcessor<ConcreteThree>, ConcreteThreeProcessor>(); builder.Services.AddScoped<IModelProcessor<MyAbstractClass>>(sp => sp.GetRequiredService<IModelProcessor<ConcreteThree>>());
方案2:将接口改为逆变泛型
如果你的业务逻辑允许(即泛型参数仅用作方法输入,无返回值场景),可以给IModelProcessor添加in关键字标记逆变:
public interface IModelProcessor<in S> where S : MyAbstractClass { void ProcessModel(S myAbstractClass); }
逆变允许IModelProcessor<ConcreteOne>隐式转换为IModelProcessor<MyAbstractClass>,此时你原来的注册方式即可生效,DI容器会自动将所有具体处理器注入到IEnumerable<IModelProcessor<MyAbstractClass>>集合中。
方案3:基于类型字典的工厂模式(更灵活)
若上述方案不适用,可修改工厂逻辑,通过反射识别处理器对应的模型类型,维护一个类型映射字典:
修改工厂类
public class ModelProcessorFactory : IModelProcessorFactory { private readonly Dictionary<Type, IModelProcessor<MyAbstractClass>> _processorLookup; public ModelProcessorFactory(IEnumerable<object> allProcessors) { _processorLookup = allProcessors .OfType<IModelProcessor<MyAbstractClass>>() .ToDictionary(processor => // 提取处理器对应的泛型模型类型 processor.GetType() .GetInterfaces() .First(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IModelProcessor<>)) .GetGenericArguments()[0]); } // 新增方法:根据模型类型获取对应处理器 public IModelProcessor<MyAbstractClass> GetProcessorForModel(Type modelType) { _processorLookup.TryGetValue(modelType, out var processor); return processor; } }
保持原有注册方式
builder.Services.AddScoped<IModelProcessor<ConcreteOne>, ConcreteOneProcessor>(); builder.Services.AddScoped<IModelProcessor<ConcreteTwo>, ConcreteTwoProcessor>(); builder.Services.AddScoped<IModelProcessor<ConcreteThree>, ConcreteThreeProcessor>(); builder.Services.AddScoped<IModelProcessorFactory, ModelProcessorFactory>();
这种方式无需修改泛型接口的兼容性,同时支持根据模型类型动态获取处理器。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

