如何在ServiceCollection中注册非匹配的开放泛型类型
问题场景与解决方案
问题背景
我能正常注册基础开放泛型服务:
services.AddScoped(typeof(IAsyncRepository<>), typeof(AsyncRepository<>));
但面对嵌套泛型的事件处理场景时,常规注册方式失效:
相关类型定义
// 泛型事件接口 public interface IAsyncEvent<in TEvent> where TEvent : class { Task HandleAsync(TEvent eventArg, CancellationToken ct); } // 泛型动作类 public class CreateAction<TModel>(TModel model) : ICloneable where TModel : class { public TModel Model { get; } = model; } // 事件处理类,实现IAsyncEvent<CreateAction<TModel>> public class CreateEventAsync<TModel>(IAsyncRepository<TModel> repository) : IAsyncEvent<CreateAction<TModel>> where TModel : class { public async Task HandleAsync(CreateAction<TModel> eventArg, CancellationToken ct) { await repository.CreateAsync(eventArg.Model, userContext, ct); } }
失败的注册尝试
- 运行时错误:
services.AddScoped( typeof(IAsyncEvent<>).MakeGenericType(typeof(CreateAction<>)), typeof(CreateEventAsync<>) );
- 编译错误:
services.AddScoped( typeof(IAsyncEvent<CreateAction<>)), typeof(CreateEventAsync<>) );
需要找到能正确注册该嵌套开放泛型服务的方法,同时支持获取请求的封闭泛型类型来处理额外参数。
可行解决方案
方案1:使用工厂委托注册(推荐)
通过AddScoped的工厂重载,手动解析封闭泛型类型、提取泛型参数并构建实例,这种方式能灵活处理额外参数:
services.AddScoped(typeof(IAsyncEvent<>), (serviceProvider, requestedServiceType) => { // 提取IAsyncEvent<TEvent>中的TEvent类型(即CreateAction<TModel>) var eventType = requestedServiceType.GetGenericArguments()[0]; // 从CreateAction<TModel>中提取TModel类型 var modelType = eventType.GetGenericArguments()[0]; // 构建CreateEventAsync<TModel>的封闭泛型类型 var handlerConcreteType = typeof(CreateEventAsync<>).MakeGenericType(modelType); // 解析构造函数依赖(比如IAsyncRepository<TModel>) var repository = serviceProvider.GetRequiredService( typeof(IAsyncRepository<>).MakeGenericType(modelType) ); // 若有其他参数(如userContext),可在此处补充获取逻辑 // var userContext = serviceProvider.GetRequiredService<IUserContext>(); // 创建并返回处理类实例 return Activator.CreateInstance(handlerConcreteType, repository); });
方案2:理解原生DI的开放泛型匹配逻辑
原生.NET DI支持嵌套开放泛型注册,但要求服务的泛型参数与实现类的泛型参数直接一一对应。由于CreateEventAsync<TModel>实现的是IAsyncEvent<CreateAction<TModel>>,两者泛型参数是间接映射(TModel → CreateAction<TModel>),直接注册开放泛型会导致DI无法匹配,因此工厂方法是更可靠的选择。
如果你的场景没有额外参数需要处理,也可以考虑自定义扩展方法来封装上述逻辑,但工厂委托已经足够灵活。
内容的提问来源于stack exchange,提问作者Luiz Bicalho
相关产品推荐
相关产品推荐

