I recently started making new app using Clean Architecture + CQRS with MediatR. This is my first attempt to this architecture and I want to implement this architecture without using any templates, because I want to understand how it works. Currently I am trying to implement FluentValidation to my app but one think that I know for sure is that it is not working :(
My github repo for broader view https://github.com/szymonJag/Chatter_v1
I am trying to follow Milan Jovanovic tutorials from YT. (https://www.youtube.com/watch?v=85dxwd8HzEk&t)
So I created ValidationBehavior class in my Application Project exactly like it looks in tutorial.
public async Task<TResponse> Handle(TRequest request, RequestHandlerDelegate<TResponse> next, CancellationToken cancellationToken) { if (!validators.Any()) { return await next(); }
Error[] errors = validators
.Select(validator => validator.Validate(request))
.SelectMany(validationResult => validationResult.Errors)
.Where(validationFailure => validationFailure is not null)
.Select(failure => new Error(failure.PropertyName, failure.ErrorMessage))
.Distinct().ToArray();
if (errors.Any())
{
return CreateValidationResult<TResponse>(errors);
}
return await next();
}
private static TResult CreateValidationResult<TResult>(Error[] errors) where TResult : Result
{
if (typeof(TResult) == typeof(Result))
{
return (ValidationResult.WithErrors(errors) as TResult)!;
}
object validationResult = typeof(ValidationResult<>)
.GetGenericTypeDefinition()
.MakeGenericType(typeof(TResult).GenericTypeArguments[0])
.GetMethod(nameof(ValidationResult.WithErrors))!
.Invoke(null, new object?[] { errors })!;
return (TResult)validationResult;
}
Then I have CreateTodoCommand and CreateTodoCommandValidator
public sealed record class CreateTodoCommand(string Title, string Description, PriorityLevel Priority) : ICommand<Guid>{}
internal class CreateTodoCommandValidator : AbstractValidator<CreateTodoCommand>
{
public CreateTodoCommandValidator()
{
RuleFor(x => x.Title).NotEmpty().MinimumLength(2);
RuleFor(x => x.Description).NotEmpty();
RuleFor(x => x.Priority).NotEmpty();
}
}
My problem is that the validation is not working when I am trying to create todo and todo is added to database. When I am debbuging my app, debugger is not even reaching Handle method from ValidationBehavior. Below how I am injecting behavior in Application
public static class DependencyInjection
{
public static Assembly Assembly { get; set; } = typeof(DependencyInjection).Assembly;
public static IServiceCollection AddApplication(this IServiceCollection services)
{
services.AddAutoMapper(Assembly.GetExecutingAssembly());
services.AddValidatorsFromAssembly(Assembly.GetExecutingAssembly(), includeInternalTypes: true);
services.AddMediatR(cfg =>
{
cfg.RegisterServicesFromAssembly(Assembly);
cfg.AddBehavior(typeof(IPipelineBehavior<,>), typeof(ValidationBehavior<,>));
});
return services;
}
}
Then in Program.cs
builder.Services.AddApplication();