如何注册MediatR泛型IRequestHandler?已遇服务未注册错误
问题:MediatR泛型处理器注册失败,提示"No service registered"
尝试使用MediatR的泛型IRequestHandler实现通用查询处理器,搭配不同规约返回不同DTO列表,但调用端点时出现错误:No service for type 'MediatR.IRequestHandler2[GetUsersQuery1[UserDto],System.Collections.Generic.List1[UserDto]]' has been registered.`
以下是可复现的代码示例:
核心代码片段
Program.cs
using MediatR; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; var builder = WebApplication.CreateBuilder(args); builder.Services.AddEndpointsApiExplorer(); builder.Services.AddSwaggerGen(); string connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string is missing"); builder.Services.AddDbContext<IApplicationDbContext, AppDbContext>(options => options.UseNpgsql(connectionString)); // 原注册方式 builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); var app = builder.Build(); if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); } app.UseHttpsRedirection(); app.MapGet("/Users/Adults", ([FromServices] IMediator mediator) => { return mediator.Send(new GetUsersQuery<UserDto>(new AdultSpecification())); }).Produces<List<UserDto>>() .WithName("GetAdultUsers") .WithOpenApi(); app.MapGet("/Users/GmailAccounts", ([FromServices] IMediator mediator) => { return mediator.Send(new GetUsersQuery<GmailDto>(new GmailAccountSpecification())); }).Produces<List<GmailDto>>() .WithName("GetGmailUsers") .WithOpenApi(); app.Run();
泛型查询与处理器
// GetUsersQuery.cs public class GetUsersQuery<T> : IRequest<List<T>> where T : class { public ISpecification<User, T> Specification { get; } public GetUsersQuery(ISpecification<User, T> specification) { Specification = specification; } } // GetUsersQueryHandler.cs public class GetUsersQueryHandler<T> : IRequestHandler<GetUsersQuery<T>, List<T>> where T : class { private readonly IApplicationDbContext _context; public GetUsersQueryHandler(IApplicationDbContext context) { _context = context; } public async Task<List<T>> Handle(GetUsersQuery<T> request, CancellationToken cancellationToken) { return await _context.Users .WithSpecification(request.Specification).ToListAsync(cancellationToken: cancellationToken); } }
错误信息
Exception has occurred: `CLR/System.InvalidOperationException` An exception of type `System.InvalidOperationException` occurred in Microsoft.Extensions.DependencyInjection.Abstractions.dll but was not handled in user code: `No service for type 'MediatR.IRequestHandler`2[GetUsersQuery`1[UserDto],System.Collections.Generic.List`1[UserDto]]' has been registered.` at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType) at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider) at MediatR.Wrappers.RequestHandlerWrapperImpl`2.<>c__DisplayClass1_0.<Handle>g__Handler|0() at MediatR.Wrappers.RequestHandlerWrapperImpl`2.Handle(IRequest`1 request, IServiceProvider serviceProvider, CancellationToken cancellationToken) at MediatR.Mediator.Send[TResponse](IRequest`1 request, CancellationToken cancellationToken) at Program.<>c.<<Main>$>b__0_2(IMediator mediator) in /home/hardik/code/dotnet/sample-app/src/Program.cs:line 34
解决方案
原因
MediatR默认的程序集扫描(RegisterServicesFromAssembly)只会自动注册封闭泛型的处理器(即已指定具体类型参数的泛型实现),而GetUsersQueryHandler<T>是开放泛型(未绑定具体T类型),因此无法被自动发现并注册到DI容器中。
修复步骤
在Program.cs的服务注册部分,手动添加开放泛型处理器的注册,替换原有的MediatR注册代码为:
builder.Services.AddMediatR(cfg => cfg.RegisterServicesFromAssembly(typeof(Program).Assembly)); // 手动注册开放泛型查询处理器 builder.Services.AddScoped(typeof(IRequestHandler<GetUsersQuery<>, List<>>), typeof(GetUsersQueryHandler<>));
说明
typeof(GetUsersQuery<>)和typeof(List<>)表示开放泛型类型(未指定具体T)- 此注册方式告诉DI容器:当需要任何
IRequestHandler<GetUsersQuery<T>, List<T>>类型的服务时,使用GetUsersQueryHandler<T>的实例来满足需求 - 保留原有
AddMediatR调用,确保其他非泛型/封闭泛型的MediatR组件仍能被自动注册
修改完成后,重新启动应用,调用/Users/Adults或/Users/GmailAccounts端点即可正常返回数据。
内容的提问来源于stack exchange,提问作者Hardik Dhuri
相关产品推荐
相关产品推荐

