如何自动注册MediatR泛型处理器,无需手动定义每种类型?
背景与现状
我在ASP.NET Core中结合MediatR和依赖注入开发时,遇到了泛型处理器注册的扩展性问题。当前的注册方式需要手动为每种具体类型(如string、bool、decimal等)显式添加处理器,代码如下:
public static void MediatRServices(this IServiceCollection service) { service.AddMediatR(c => c.RegisterServicesFromAssembly(typeof(LoginCommand).Assembly)); service.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); // 手动为不同具体类型添加处理器 service.AddTransient(typeof(IRequestHandler<UpdateServicePropertyCommand<string>, GenericResponseDto>), typeof(UpdateServiceHandler<string>)); service.AddTransient(typeof(IRequestHandler<UpdateServicePropertyCommand<bool>, GenericResponseDto>), typeof(UpdateServiceHandler<bool>)); service.AddTransient(typeof(IRequestHandler<UpdateServicePropertyCommand<decimal>, GenericResponseDto>), typeof(UpdateServiceHandler<decimal>)); service.AddTransient(typeof(IRequestHandler<UpdateServicePropertyCommand<TimeSpan>, GenericResponseDto>), typeof(UpdateServiceHandler<TimeSpan>)); // 其他命令的处理器... service.AddTransient(typeof(IRequestHandler<UpdateGenericPropertyCommand<ServiceTemplate, bool>, GenericResponseDto>), typeof(UpdateGenericPropertyHandler<ServiceTemplate, bool>)); service.AddTransient(typeof(IRequestHandler<UpdateGenericPropertyCommand<ServiceTemplate, decimal>, GenericResponseDto>), typeof(UpdateGenericPropertyHandler<ServiceTemplate, decimal>)); service.AddTransient(typeof(IRequestHandler<UpdateGenericPropertyCommand<ServiceTemplate, TimeSpan>, GenericResponseDto>), typeof(UpdateGenericPropertyHandler<ServiceTemplate, TimeSpan>)); }
对应的命令和处理器定义如下:
public class UpdateServicePropertyCommand<T> : IRequest<GenericResponseDto> { public T Value { get; set; } } public class UpdateServiceHandler<T> : IRequestHandler<UpdateServicePropertyCommand<T>, GenericResponseDto> { public Task<GenericResponseDto> Handle(UpdateServicePropertyCommand<T> request, CancellationToken cancellationToken) { // 业务实现 } }
问题与尝试
手动注册每个泛型类型组合的扩展性极差,当类型组合增多时维护成本飙升。我曾尝试直接注册开放式泛型,但ASP.NET Core原生DI不支持这种方式,运行时会报错:
services.AddTransient(typeof(IRequestHandler<UpdateGenericPropertyCommand<,>, GenericResponseDto>), typeof(UpdateGenericPropertyHandler<,>));
另外,我尝试启用MediatR的RegisterGenericHandlers配置:
service.AddMediatR(c => { c.RegisterServicesFromAssembly(typeof(LoginCommand).Assembly); c.MaxTypesClosing = 0; // 允许注册所有类型 c.RegisterGenericHandlers = true; });
但触发了错误:
System.ArgumentException: Error registering the generic type: . The total number of generic type registrations exceeds the maximum allowed (125000).
该配置会尝试生成过多类型,超出上限。
疑问
- 有没有更优的方式统一配置注册,无需手动定义每种类型组合?
- 是否可以通过程序集扫描动态注册所有匹配
IRequestHandler<,>的泛型处理器? - 如果Autofac等第三方DI容器更适合,该如何简化泛型注册?
解决方案
方案1:使用MediatR的泛型注册支持(无需额外库)
MediatR本身支持注册开放式泛型处理器,无需手动指定具体类型。只需确保你的泛型处理器实现了正确的IRequestHandler<,>接口,然后直接注册开放式泛型即可:
public static void MediatRServices(this IServiceCollection service) { service.AddMediatR(c => c.RegisterServicesFromAssembly(typeof(LoginCommand).Assembly)); service.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); // 注册开放式泛型处理器 service.AddTransient( typeof(IRequestHandler<UpdateServicePropertyCommand<>, GenericResponseDto>), typeof(UpdateServiceHandler<>) ); service.AddTransient( typeof(IRequestHandler<UpdateGenericPropertyCommand<,>, GenericResponseDto>), typeof(UpdateGenericPropertyHandler<,>) ); }
注意:ASP.NET Core DI支持开放式泛型注册,但需要确保接口和实现的泛型参数数量完全匹配。比如UpdateServicePropertyCommand<T>是单泛型参数,对应的处理器UpdateServiceHandler<T>也要匹配,注册时用UpdateServicePropertyCommand<>(开放式泛型)而非具体类型。
方案2:使用Scrutor进行程序集扫描
Scrutor可以自动扫描程序集中的泛型类型并注册,只需配置扫描规则:
- 安装Scrutor NuGet包:
Install-Package Scrutor - 配置服务注册:
public static void MediatRServices(this IServiceCollection service) { service.AddMediatR(c => c.RegisterServicesFromAssembly(typeof(LoginCommand).Assembly)); service.AddTransient(typeof(IPipelineBehavior<,>), typeof(LoggingBehavior<,>)); // 扫描程序集,自动注册泛型处理器 service.Scan(scan => scan .FromAssemblyOf<UpdateServiceHandler<>>() .AddClasses(classes => classes.AssignableTo(typeof(IRequestHandler<,>))) .AsImplementedInterfaces() .WithTransientLifetime() ); }
Scrutor会自动识别所有实现IRequestHandler<,>的泛型类,并按开放式泛型的方式注册,无需手动指定具体类型。
方案3:使用Autofac简化泛型注册
如果迁移到Autofac,开放式泛型注册会更简洁:
- 安装Autofac相关NuGet包:
Install-Package Autofac.Extensions.DependencyInjection - 在Program.cs中配置Autofac作为DI容器:
var builder = WebApplication.CreateBuilder(args); builder.Host.UseServiceProviderFactory(new AutofacServiceProviderFactory()); builder.Host.ConfigureContainer<ContainerBuilder>(containerBuilder => { // 注册MediatR containerBuilder.RegisterMediatR(typeof(LoginCommand).Assembly); // 注册开放式泛型处理器 containerBuilder.RegisterGeneric(typeof(UpdateServiceHandler<>)) .As(typeof(IRequestHandler<UpdateServicePropertyCommand<>, GenericResponseDto>)) .InstancePerDependency(); containerBuilder.RegisterGeneric(typeof(UpdateGenericPropertyHandler<,>)) .As(typeof(IRequestHandler<UpdateGenericPropertyCommand<,>, GenericResponseDto>)) .InstancePerDependency(); // 或者自动扫描所有泛型处理器 containerBuilder.RegisterAssemblyTypes(typeof(LoginCommand).Assembly) .AsClosedTypesOf(typeof(IRequestHandler<,>)) .InstancePerDependency(); });
Autofac的RegisterGeneric和AsClosedTypesOf可以轻松处理开放式泛型注册,AsClosedTypesOf会自动为所有匹配的闭合泛型类型注册对应的处理器。
内容的提问来源于stack exchange,提问作者Antonio Pisarra

