MS DI+MediatR:为带泛型响应的基础IRequest配置PipelineBehavior问题
我想给基础请求添加一个PipelineBehavior,用来验证调用者是否有权限访问目标租户。之前的做法是给特定请求加ITenantRequest接口,然后写一个全泛型的PipelineBehavior<,>,它会拦截所有请求,判断request is ITenantRequest后再执行验证。但这种方式效率不高,我希望管道只在继承了带TenantId属性的基类的请求上运行。
运行时出现以下错误:
实现类型'TenantAccessValidationPipeline
2[SomeTenantRequest,SomeTenantResponse]'无法转换为服务类型'MediatR.IPipelineBehavior2[SomeTenantRequest,SomeTenantResponse]'
启动代码
var services = new ServiceCollection(); services.AddMediatR(typeof(Program).Assembly); services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TenantAccessValidationPipeline<,>)); var provider = services.BuildServiceProvider(); var mediator = provider.GetRequiredService<IMediator>(); mediator.Send(new SomeTenantRequest() { SomeProperty = "SomeProperty", TenantId = "CustomerId" });
类定义
public class RequestsBase<T> : IRequest<T> { public string TenantId { get; set; } } public class TenantAccessValidationPipeline<T, _> : IPipelineBehavior<RequestsBase<T>, T> { public Task<T> Handle(RequestsBase<T> request, CancellationToken ct, RequestHandlerDelegate<T> next) { Console.WriteLine(request.TenantId); return next(); } } public record SomeTenantResponse(string p); public class SomeTenantRequest : RequestsBase<SomeTenantResponse> { public string SomeProperty { get; set; } } public class SomeTenantRequestHandler : IRequestHandler<SomeTenantRequest, SomeTenantResponse> { public Task<SomeTenantResponse> Handle( SomeTenantRequest request, CancellationToken ct) { return Task.FromResult( new SomeTenantResponse(request.TenantId + request.SomeProperty)); } }
解决方案
错误原因
你的管道行为实现的是IPipelineBehavior<RequestsBase<T>, T>,但MediatR处理SomeTenantRequest时,期望的是IPipelineBehavior<SomeTenantRequest, SomeTenantResponse>。由于泛型接口是不变的,基类请求的管道无法直接适配具体请求类型,导致类型转换失败。
正确实现方式
方式一:基于基类的泛型约束(推荐)
调整管道行为的泛型定义,给请求类型添加继承约束,确保它仅适配继承RequestsBase<TResponse>的请求:
public class TenantAccessValidationPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : RequestsBase<TResponse> { public Task<TResponse> Handle(TRequest request, CancellationToken ct, RequestHandlerDelegate<TResponse> next) { Console.WriteLine(request.TenantId); // 在这里添加租户权限验证逻辑 return next(); } }
服务注册保持原有开放泛型方式即可:
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(TenantAccessValidationPipeline<,>));
DI容器会自动识别符合泛型约束的请求类型,仅为这些请求创建管道行为实例,无需拦截所有请求。
方式二:接口标记+泛型约束(灵活替代)
如果需要更灵活的适配(比如部分类不继承基类但需要租户验证),可以给基类实现标记接口,再通过约束筛选:
public interface ITenantRequest { string TenantId { get; } } public class RequestsBase<T> : IRequest<T>, ITenantRequest { public string TenantId { get; set; } } public class TenantAccessValidationPipeline<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse> where TRequest : ITenantRequest { public Task<TResponse> Handle(TRequest request, CancellationToken ct, RequestHandlerDelegate<TResponse> next) { Console.WriteLine(request.TenantId); return next(); } }
注册方式同上,DI会自动为实现ITenantRequest的请求应用管道行为。
这两种方式都能避免对所有请求做类型判断,提升运行效率,根据你的实际场景选择即可。
内容的提问来源于stack exchange,提问作者LLL

