I have a MediatR pipeline behavior like this:
public class FailFastRequestBehavior<TRequest, TResponse> : IPipelineBehavior<TRequest, TResponse>
{
private readonly IEnumerable<IValidator> _validators;
public FailFastRequestBehavior(IEnumerable<IValidator<TRequest>> validators)
{
_validators = validators;
}
public Task<TResponse> Handle(TRequest request, CancellationToken cancellationToken, RequestHandlerDelegate<TResponse> next)
{
var failures = _validators
.Select(async v => await v.ValidateAsync(request))
.SelectMany(result => result.Result.Errors)
.Where(f => f != null);
return failures.Any()
? Errors(failures)
: next();
}
...
}
And MediatR commands like this:
public class MyUseCase
{
public class Command : IRequest<CommandResponse>
{
...
}
public class Validator : AbstractValidator<Command>
{
...
}
public class Handler<T>: IRequestHandler<T, CommandResponse>
{
...
}
}
The validators are registered on Startup.cs
like this:
AssemblyScanner
.FindValidatorsInAssembly(Assembly.GetAssembly(typeof(MyUseCase)))
.ForEach(result =>
services.AddScoped(result.InterfaceType, result.ValidatorType));
This works nice for the MyUseCase.Validator
, it is injected on the pipeline and is executed, validating the MyUseCase.Command
.
But it's a large application, and many commands have common properties, i.e. every order operation receives an OrderId
and I have to check if the Id is valid, if the entity exists in database, if the authenticated user is the owner of the order being modified, etc.
So I tried to create the following interface and validator:
public interface IOrder
{
string OrderId { get; set; }
}
public class IOrderValidator : AbstractValidator<IOrder>
{
public IOrderValidator()
{
CascadeMode = CascadeMode.StopOnFirstFailure;
RuleFor(x => x.OrderId)
.Rule1()
.Rule2()
.Rule3()
.RuleN()
}
}
Finally I changed the command to this:
public class MyUseCase
{
public class Command : IRequest<CommandResponse>: IOrder
{
...
}
public class Validator : AbstractValidator<Command>
{
...
}
public class Handler<T>: IRequestHandler<T, CommandResponse>
{
...
}
}
The problem is that the IOrderValidator
is not injected in the pipeline, only the MyUseCase.Validator
is.
Am I missing something here or is it even possible to inject multiple validators in the pipeline?