I am using MediatR with the following 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; }
}
Where Envelope is a wrapper 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>();
}
The GetPostsRequest, sent by the mediator, is executed by the Handler:
public class GetPostsRequestHandler : IRequestHandler<GetPostsRequest, Envelope<GetPostsResponse>> {
public async Task<Envelope<GetPostsResponse>> Handle(GetPostsRequest request, CancellationToken cancellationToken) {
}
}
MediatR allows the use of Behaviors which executes before the Handler of a specific Request. I created a ValidationBehavior as follows:
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();
}
}
And I registered the ValidationBehavior on the ASP.NET Core application:
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
When I call the API I get the following error:
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'.
What am I missing?
services.AddScoped(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
I don’t think this line does what you expect it to do.
In your example, your request type is a IRequest<Envelope<Response>>
, so when MediatR is looking for a pipeline behavior, it will look for a IPipelineBehavior<Request, Envelope<Response>>
.
Since there is a open-generic registration in the DI container for IPipelineBehavior<,>
, that implementation will be used. So the generic type arguments from IPipelineBehavior<,>
will be used to instantiate a type of ValidationBehavior<,>
.
So for IPipelineBehavior<Request, Envelope<Response>>
, this will create a ValidationBehavior<Request, Envelope<Response>>
. This means that in your generic implementation, TRequest
will be Request
, and TResponse
will be Envelope<Response>
. This however means that the type constaint on your type means that TRequest
, or Request
in this case, needs to implement IRequest<Envelope<Envelope<Response>>>
. Of course, that is not the case and as such that explains the type constraint violation you are seeing.
Unfortunately, this means that you cannot do it the way you want it to work. You cannot put a type constraint on your generic type, at least not one for Envelope<T>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With