MediatR 行为的约束违反异常
Constraint Violated Exception on MediatR Behavior
我将 MediatR 与以下 classes 一起使用:
public class GetPostsRequest : IRequest<Envelope<GetPostsResponse>> {
public Int32 Age { get; set; }
}
public class GetPostResponse {
public String Title { get; set; }
public String Content { get; set; }
}
其中 Envelope 是包装器 class:
public class Envelope<T> {
public List<T> Result { get; private set; } = new List<T>();
public List<Error> Errors { get; private set; } = new List<Error>();
}
中介者发送的GetPostsRequest,由Handler执行:
public class GetPostsRequestHandler : IRequestHandler<GetPostsRequest, Envelope<GetPostsResponse>> {
public async Task<Envelope<GetPostsResponse>> Handle(GetPostsRequest request, CancellationToken cancellationToken) {
}
}
MediatR 允许使用在特定请求的处理程序之前执行的行为。我创建了一个 ValidationBehavior 如下:
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, Envelope<TResponse>>
where TRequest : IRequest<Envelope<TResponse>> {
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators) {
_validators = validators;
}
public Task<Envelope<TResponse>> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<Envelope<TResponse>> next) {
ValidationContext context = new ValidationContext(request);
List<Error> errors = _validators
.Select(x => x.Validate(context))
.SelectMany(x => x.Errors)
.Select(x => new Error(ErrorCode.DataNotValid, x.ErrorMessage, x.PropertyName))
.ToList();
if (errors.Any())
return Task.FromResult<Envelope<TResponse>>(new Envelope<TResponse>(errors));
return next();
}
}
并且我在 ASP.NET 核心应用程序上注册了 ValidationBehavior:
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
当我调用 API 时,出现以下错误:
An unhandled exception has occurred while executing the request.
System.ArgumentException: GenericArguments[0], 'TRequest', on 'ValidationBehavior`2[TRequest,TResponse]' violates the constraint of type 'TRequest'.
---> System.TypeLoadException: GenericArguments[0], 'TRequest', on 'ValidationBehavior`2[TRequest,TResponse]' violates the constraint of type parameter 'TRequest'.
我错过了什么?
您的 ValidationBehavior
指定 TRequest
必须是 IRequest<Envelope<TResponse>>
您的请求实现 IRequest<Envelope<Response>>
(不匹配 - Envelope<TResponse>
不是 Envelope<Response>
)
但是,您的 ValidationBehavior 实现 IPipelineBehavior<TRequest, Envelope<TResponse>>
并且有约束 where TRequest : IRequest<Envelope<TResponse>>
因此,将 Envelope<TResponse>
更改为 Envelope<Response>
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
我认为这条线没有达到您的预期。
在您的示例中,您的请求类型是 IRequest<Envelope<Response>>
,因此当 MediatR 查找管道行为时,它将查找 IPipelineBehavior<Request, Envelope<Response>>
.
由于 IPipelineBehavior<,>
的 DI 容器中有一个开放式通用注册,因此将使用该实现。因此 IPipelineBehavior<,>
的泛型类型参数将用于实例化 ValidationBehavior<,>
.
的类型
所以对于 IPipelineBehavior<Request, Envelope<Response>>
,这将创建一个 ValidationBehavior<Request, Envelope<Response>>
。这意味着在您的通用实现中,TRequest
将是 Request
,而 TResponse
将是 Envelope<Response>
。然而,这意味着您的类型的类型约束意味着 TRequest
,或本例中的 Request
,需要实现 IRequest<Envelope<Envelope<Response>>>
。当然,情况并非如此,因此可以解释您所看到的类型约束冲突。
不幸的是,这意味着您无法按照您希望的方式进行操作。您不能对泛型类型施加类型约束,至少不能为 Envelope<T>
.
我将 MediatR 与以下 classes 一起使用:
public class GetPostsRequest : IRequest<Envelope<GetPostsResponse>> {
public Int32 Age { get; set; }
}
public class GetPostResponse {
public String Title { get; set; }
public String Content { get; set; }
}
其中 Envelope 是包装器 class:
public class Envelope<T> {
public List<T> Result { get; private set; } = new List<T>();
public List<Error> Errors { get; private set; } = new List<Error>();
}
中介者发送的GetPostsRequest,由Handler执行:
public class GetPostsRequestHandler : IRequestHandler<GetPostsRequest, Envelope<GetPostsResponse>> {
public async Task<Envelope<GetPostsResponse>> Handle(GetPostsRequest request, CancellationToken cancellationToken) {
}
}
MediatR 允许使用在特定请求的处理程序之前执行的行为。我创建了一个 ValidationBehavior 如下:
public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, Envelope<TResponse>>
where TRequest : IRequest<Envelope<TResponse>> {
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidationBehavior(IEnumerable<IValidator<TRequest>> validators) {
_validators = validators;
}
public Task<Envelope<TResponse>> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<Envelope<TResponse>> next) {
ValidationContext context = new ValidationContext(request);
List<Error> errors = _validators
.Select(x => x.Validate(context))
.SelectMany(x => x.Errors)
.Select(x => new Error(ErrorCode.DataNotValid, x.ErrorMessage, x.PropertyName))
.ToList();
if (errors.Any())
return Task.FromResult<Envelope<TResponse>>(new Envelope<TResponse>(errors));
return next();
}
}
并且我在 ASP.NET 核心应用程序上注册了 ValidationBehavior:
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
当我调用 API 时,出现以下错误:
An unhandled exception has occurred while executing the request.
System.ArgumentException: GenericArguments[0], 'TRequest', on 'ValidationBehavior`2[TRequest,TResponse]' violates the constraint of type 'TRequest'.
---> System.TypeLoadException: GenericArguments[0], 'TRequest', on 'ValidationBehavior`2[TRequest,TResponse]' violates the constraint of type parameter 'TRequest'.
我错过了什么?
您的 ValidationBehavior
指定 TRequest
必须是 IRequest<Envelope<TResponse>>
您的请求实现 IRequest<Envelope<Response>>
(不匹配 - Envelope<TResponse>
不是 Envelope<Response>
)
但是,您的 ValidationBehavior 实现 IPipelineBehavior<TRequest, Envelope<TResponse>>
并且有约束 where TRequest : IRequest<Envelope<TResponse>>
因此,将 Envelope<TResponse>
更改为 Envelope<Response>
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
我认为这条线没有达到您的预期。
在您的示例中,您的请求类型是 IRequest<Envelope<Response>>
,因此当 MediatR 查找管道行为时,它将查找 IPipelineBehavior<Request, Envelope<Response>>
.
由于 IPipelineBehavior<,>
的 DI 容器中有一个开放式通用注册,因此将使用该实现。因此 IPipelineBehavior<,>
的泛型类型参数将用于实例化 ValidationBehavior<,>
.
所以对于 IPipelineBehavior<Request, Envelope<Response>>
,这将创建一个 ValidationBehavior<Request, Envelope<Response>>
。这意味着在您的通用实现中,TRequest
将是 Request
,而 TResponse
将是 Envelope<Response>
。然而,这意味着您的类型的类型约束意味着 TRequest
,或本例中的 Request
,需要实现 IRequest<Envelope<Envelope<Response>>>
。当然,情况并非如此,因此可以解释您所看到的类型约束冲突。
不幸的是,这意味着您无法按照您希望的方式进行操作。您不能对泛型类型施加类型约束,至少不能为 Envelope<T>
.