FluentValidation · How it works

1 min read
Mid-level6 min read
Rapid overview

How it works

This note covers how to use FluentValidation properly and what a senior C#/.NET developer needs to know: core concepts, best practices, integration with ASP.NET Core, testing, performance, and advanced extension points.


Core concepts and API

  • Create a validator by implementing AbstractValidator<T>.
  • Add rules with RuleFor(x => x.Property).NotEmpty().Length(5, 50).Must(...);
  • Supports When, Unless, CascadeMode and RuleSet for conditional and grouped validation.
  • Validators support async via MustAsync, custom IValidator<T> implementations, and SetValidator to compose nested validators.

Example:

public class CreateOrderDto
{
    public string Symbol { get; set; }
    public decimal Amount { get; set; }
    public string CustomerId { get; set; }
}

public class CreateOrderDtoValidator : AbstractValidator<CreateOrderDto>
{
    private readonly ILeaveTypeRepository _leaveTypeRepository;

    public CreateOrderDtoValidator(ILeaveTypeRepository leaveTypeRepository)
    {
        CascadeMode = CascadeMode.Stop;

        RuleFor(x => x.Symbol)
            .NotEmpty()
            .Length(3, 10);

        RuleFor(x => x.Amount)
            .GreaterThan(0);

        RuleFor(x => x.CustomerId)
            .NotEmpty()
            .MustAsync(async (id, ct) => await CustomerExists(id))
            .WithMessage("Customer does not exist");

        // Example of nested validator
        RuleFor(x => x.Address).SetValidator(new AddressValidator());
        RuleFor(q => q)
        .MustAsync(LeaveTypeNameUnique).WithMessage("Leave type already exists");

        _leaveTypeRepository = leaveTypeRepository;
    }

    private async Task<bool> LeaveTypeNameUnique(CreateLeaveTypeCommand command, CancellationToken token)
    {
        return await _leaveTypeRepository.IsLeaveTypeUniqe(command.Name); 
    }
}

See also