Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Constraint Violated Exception on MediatR Behavior

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?

like image 842
Miguel Moura Avatar asked Aug 29 '18 14:08

Miguel Moura


1 Answers

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>.

like image 127
poke Avatar answered Oct 28 '22 03:10

poke