Mediatr: Constraint Violated Exception on MediatR Behavior

Created on 29 Aug 2018  路  4Comments  路  Source: jbogard/MediatR

I have the following classes:

public class GetPostsRequest : IRequest<Envelope<GetPostsResponse>> {
  public Int32 Age { get; set; }
}

public class GetPostsResponse {
  public String Code { get; set; }
  public String Name { 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>();
}

Then I created a ValidationBehavior using FluentValidation:

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

Am I missing something?

Most helpful comment

ASP.NET Core DI does not support constrained open generics. I've opened a PR to add support:

https://github.com/aspnet/DependencyInjection/pull/635

In the meantime, you'll have to switch to one of the 8-9 other containers that do support this feature:

https://github.com/jbogard/MediatR/wiki/Container-Feature-Support

The only containers that don't support this are Ninject (dead) and MS.Extensions.DI (alive).

All 4 comments

ASP.NET Core DI does not support constrained open generics. I've opened a PR to add support:

https://github.com/aspnet/DependencyInjection/pull/635

In the meantime, you'll have to switch to one of the 8-9 other containers that do support this feature:

https://github.com/jbogard/MediatR/wiki/Container-Feature-Support

The only containers that don't support this are Ninject (dead) and MS.Extensions.DI (alive).

@jbogard I switched to Autofac and used the following configuration:

  var builder = new ContainerBuilder();

  builder.Populate(services);

  builder.RegisterType<Mediator>().As<IMediator>().InstancePerLifetimeScope();

  builder.RegisterAssemblyTypes(typeof(Startup).Assembly)
             .AsClosedTypesOf(typeof(IRequestHandler<,>))
             .AsImplementedInterfaces()
             .InstancePerDependency();

  builder.RegisterGeneric(typeof(ValidationBehavior<,>))
             .As(typeof(IPipelineBehavior<,>))
             .InstancePerLifetimeScope();

  builder.Register<ServiceFactory>(x => {
    IComponentContext context = x.Resolve<IComponentContext>();
    return y => context.Resolve(y);
  }).InstancePerLifetimeScope();          

  builder.RegisterAssemblyTypes(typeof(Startup).Assembly)
            .Where(x => x.Name.EndsWith("Validator"))
            .AsImplementedInterfaces()
            .InstancePerLifetimeScope();

  var container = builder.Build();

Then I tried to resolve instances for each type:

  var mediator = container.Resolve<IMediator>();

  var handler = container.Resolve<IRequestHandler<GetPostsRequest, Envelope<GetPostsResponse>>>();

  var validators = container.Resolve<IEnumerable<IValidator<GetPostsRequest>>>();

  var behaviors = container.Resolve<IEnumerable<IPipelineBehavior<GetPostsRequest, Envelope<GetPostsResponse>>>>().ToList();

I was able to get the mediator, handlers, validators ... But I do not get any behaviors.

Am I missing something?

Thank You

@mdmoura I made a small change to fix this. Add a class

public class EnvelopeRequest<T> : IRequest<Envelope<T>> { }

and then extend this class for the GetPostsRequest

public class GetPostsRequest : EnvelopeRequest<GetPostsResponse>
{
    public Int32 Age { get; set; }
}

Then the validation behavior is changed to have this constraint

public class ValidationBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, Envelope<TResponse>>
        where TRequest : EnvelopeRequest<TResponse>
{
...

This should get your behavior to be resolved.

@lilasquared Thank you for the help

Was this page helpful?
0 / 5 - 0 ratings