I've been struggling with an issue for a while now. I am building a project based on the eShopOnContainers GitHub project See Here. My project is running on asp.net core 2.2 and I am using
MediatR 6.0,
I am using MediatR commands that are handled by a command handler, and through many articles, along with the eShopOnContainers sample online, I've implemented a ValidatorBehavior
class that implements IPipelineBehavior
.
public class ValidatorBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
where TRequest : IRequest<TResponse>
{
private readonly IEnumerable<IValidator<TRequest>> _validators;
public ValidatorBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public async Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var context = new ValidationContext(request);
var failures = _validators
.Select(v => v.Validate(context))
.SelectMany(result => result.Errors)
.Where(error => error != null)
.ToList();
if (failures.Any())
{
throw new PlanningDomainException(
$"Command Validation Errors for type {typeof(TRequest).Name}", new ValidationException("Validation exception", failures));
}
var response = await next();
return response;
}
}
I've also included a MediatorModule, just as implemented in the sample project.
public class MediatorModule : Autofac.Module
{
protected override void Load(ContainerBuilder builder)
{
builder.RegisterAssemblyTypes(typeof(IMediator).GetType().Assembly)
.AsImplementedInterfaces();
// Get the assembly name
var assembly = typeof(Startup).GetType().Assembly;
// Register all the Command classes (they implement IRequestHandler) in assembly holding the Commands
builder.RegisterAssemblyTypes(assembly)
.AsClosedTypesOf(typeof(IRequestHandler<,>));
// Register the DomainEventHandler classes (they implement INotificationHandler<>)
// in assembly holding the Domain Events
builder.RegisterAssemblyTypes(assembly)
.AsClosedTypesOf(typeof(INotificationHandler<>));
// Register the Command's Validators (Validators based on FluentValidation library)
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
.AsImplementedInterfaces();
builder.Register<ServiceFactory>(context =>
{
var componentContext = context.Resolve<IComponentContext>();
return t => { object o; return componentContext.TryResolve(t, out o) ? o : null; };
});
builder.RegisterGeneric(typeof(LoggingBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(ValidatorBehavior<,>)).As(typeof(IPipelineBehavior<,>));
builder.RegisterGeneric(typeof(TransactionBehaviour<,>)).As(typeof(IPipelineBehavior<,>));
}
}
My test controller is:
[Route("api/[controller]")]
[ApiController]
public class ApplicationsController : ControllerBase
{
private readonly IMediator _mediator;
public ApplicationsController(IMediator mediator)
{
_mediator = mediator ?? throw new ArgumentNullException(nameof(mediator));
}
[HttpPost]
[ProducesResponseType((int)HttpStatusCode.OK)]
[ProducesResponseType((int)HttpStatusCode.BadRequest)]
public async Task<IActionResult> Put([FromBody]ApplicationCreateCommand command, [FromHeader(Name = "x-requestid")] string requestId)
{
var c = await _mediator.Send(command);
return c ? Ok() : (IActionResult)BadRequest();
}
}
I have the following issues:
Whenever I attempt to call this API I get the following error:
Cannot resolve parameter 'MediatR.IMediator mediator' of constructor 'Void .ctor(MediatR.IMediator)'.
I am solving this by adding mediator as a service using .AddMediatR()
even though in the sample project it is never added like that.
ValidatorBehavior
is being called correctly, but the CommandValidator is not present. The _validators
list is actually empty so no validation is being made.I've also set up break points in the command validator but none are being hit.
This is my command validator:
public class ApplicationCreateCommandValidator : AbstractValidator<ApplicationCreateCommand>
{
public ApplicationCreateCommandValidator()
{
RuleFor(cmd => cmd.CategoryType).NotEmpty().Must(BeValidCategoryType).WithMessage("The category type is not valid.");
RuleFor(cmd => cmd.CompetitionId).NotEmpty().WithMessage("The competition id must be specified.");
RuleFor(cmd => cmd.ParticipantId).NotEmpty().WithMessage("The participant id must be specified.");
}
private bool BeValidCategoryType(int categoryType)
{
return categoryType != 0;
}
}
Everything should work fine! I do not understand why it wouldn't. Maybe I am not loading the command validators correctly in autofac, however, every sample code I found online point to the same registration method:
builder.RegisterAssemblyTypes(assembly)
.Where(t => t.IsClosedTypeOf(typeof(IValidator<>)))
.AsImplementedInterfaces();
I have the entire source code for this project on my git hub account if you would like to take a closer look. This is the API.
Can anybody help me understand what I'm doing wrong? It's been driving me nuts these past few days.
I have similar configuration as yours. Only difference I can find is below lines in my start.cs file
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddFluentValidation(fv =>
{
fv.RegisterValidatorsFromAssemblyContaining<MediatorModule>();
fv.RunDefaultMvcValidationAfterFluentValidationExecutes = false;
}
);
}