DEV Community

Cover image for Mastering FluentValidation in .NET Core
hamza zeryouh
hamza zeryouh

Posted on

Mastering FluentValidation in .NET Core

Introduction

Data validation is a fundamental aspect of any application, ensuring that user inputs meet the required criteria before processing. FluentValidation is a powerful .NET library that simplifies the validation process while maintaining clean and maintainable code.

In this article, we will explore:

  • Why FluentValidation?
  • Setting up FluentValidation in .NET Core
  • Writing custom validators
  • Integrating FluentValidation with CQRS
  • Handling localization for multilingual validation messages

Why FluentValidation?

Key Benefits:

Separation of Concerns – Keeps validation logic separate from business rules.
Improved Readability – Uses an expressive, fluent API for defining validation rules.
Reusable & Maintainable – Eliminates repetitive validation logic across multiple components.
Supports Complex Validations – Easily handle conditional and cross-field validations.
Built-in Localization – Supports multi-language validation messages.
Seamless Integration – Works well with ASP.NET Core and Dependency Injection.


1️⃣ Installing FluentValidation

To get started, install the FluentValidation package via NuGet:

Install-Package FluentValidation.AspNetCore
Enter fullscreen mode Exit fullscreen mode

Then, register FluentValidation in Program.cs:

var builder = WebApplication.CreateBuilder(args);

// Register FluentValidation
builder.Services.AddControllers();
builder.Services.AddFluentValidationAutoValidation();
builder.Services.AddValidatorsFromAssemblyContaining<CarValidator>();

var app = builder.Build();
app.UseAuthorization();
app.MapControllers();
app.Run();
Enter fullscreen mode Exit fullscreen mode

2️⃣ Creating a Validator Class

Let's define a Car model and create a validator for it:

public class Car
{
    public string Make { get; set; }
    public string Model { get; set; }
    public string VIN { get; set; }
    public int Year { get; set; }
    public decimal PricePerDay { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Writing a Validator for the Car Model

using FluentValidation;

public class CarValidator : AbstractValidator<Car>
{
    public CarValidator()
    {
        RuleFor(car => car.Make)
            .NotEmpty().WithMessage("Make is required")
            .MaximumLength(50).WithMessage("Make cannot exceed 50 characters");

        RuleFor(car => car.Model)
            .NotEmpty().WithMessage("Model is required")
            .MaximumLength(50).WithMessage("Model cannot exceed 50 characters");

        RuleFor(car => car.VIN)
            .NotEmpty().WithMessage("VIN is required")
            .Matches("^[A-HJ-NPR-Z0-9]{17}$").WithMessage("VIN must be exactly 17 characters");

        RuleFor(car => car.Year)
            .InclusiveBetween(1886, DateTime.UtcNow.Year)
            .WithMessage($"Year must be between 1886 and {DateTime.UtcNow.Year}");

        RuleFor(car => car.PricePerDay)
            .GreaterThan(0).WithMessage("Price per day must be greater than zero");
    }
}
Enter fullscreen mode Exit fullscreen mode

3️⃣ Using FluentValidation in Controllers

[ApiController]
[Route("api/cars")]
public class CarController : ControllerBase
{
    [HttpPost]
    public IActionResult CreateCar([FromBody] Car car)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }
        return Ok("Car successfully created");
    }
}
Enter fullscreen mode Exit fullscreen mode

With FluentValidation, validation happens automatically, and invalid requests return structured error messages.


4️⃣ Advanced: Integrating FluentValidation with CQRS

In a CQRS (Command Query Responsibility Segregation) architecture, FluentValidation helps validate commands before they reach the handler.

Define a CreateCarCommand:

using MediatR;
public class CreateCarCommand : IRequest<int>
{
    public string Make { get; set; }
    public string Model { get; set; }
    public int Year { get; set; }
}
Enter fullscreen mode Exit fullscreen mode

Create a Validator for the Command:

public class CreateCarCommandValidator : AbstractValidator<CreateCarCommand>
{
    public CreateCarCommandValidator()
    {
        RuleFor(command => command.Make).NotEmpty().WithMessage("Make is required");
        RuleFor(command => command.Model).NotEmpty().WithMessage("Model is required");
        RuleFor(command => command.Year).InclusiveBetween(1886, DateTime.UtcNow.Year);
    }
}
Enter fullscreen mode Exit fullscreen mode

Register and Validate in a Handler:

public class CreateCarCommandHandler : IRequestHandler<CreateCarCommand, int>
{
    public async Task<int> Handle(CreateCarCommand request, CancellationToken cancellationToken)
    {
        // Business logic for car creation
        return await Task.FromResult(1);
    }
}
Enter fullscreen mode Exit fullscreen mode

Now, whenever CreateCarCommand is executed, FluentValidation ensures the request is valid before reaching the handler.


5️⃣ Localization Support (Multi-language Validation Messages)

To support multiple languages, integrate a translation service:

RuleFor(car => car.Make)
    .NotEmpty().WithMessage(_ => _translationService.GetTranslation("MakeRequired"));
Enter fullscreen mode Exit fullscreen mode

Example Translation Service:

public interface ITranslationService
{
    string GetTranslation(string key);
}

public class TranslationService : ITranslationService
{
    private readonly Dictionary<string, string> _translations = new()
    {
        { "MakeRequired", "La marque est requise" } // French Example
    };

    public string GetTranslation(string key)
    {
        return _translations.TryGetValue(key, out var value) ? value : key;
    }
}
Enter fullscreen mode Exit fullscreen mode

This way, validation messages can be translated dynamically based on the user's language.


Conclusion

FluentValidation is a powerful tool for managing input validation in .NET Core applications. It keeps validation logic clean, reusable, and maintainable while supporting complex business rules, CQRS integration, and localization.

🚀 Want More Advanced Logic?

Check out a full GitHub example integrating FluentValidation with CQRS:

🔗 GitHub – FluentValidation with CQRS

Are you using FluentValidation in your projects? Let’s discuss in the comments! 👇

DotNetCore #FluentValidation #Csharp #CQRS #SoftwareDevelopment #CleanCode #ASPNetCore #BackendDevelopment

Heroku

Deploy with ease. Manage efficiently. Scale faster.

Leave the infrastructure headaches to us, while you focus on pushing boundaries, realizing your vision, and making a lasting impression on your users.

Get Started

Top comments (0)

AWS Q Developer image

Your AI Code Assistant

Automate your code reviews. Catch bugs before your coworkers. Fix security issues in your code. Built to handle large projects, Amazon Q Developer works alongside you from idea to production code.

Get started free in your IDE

👋 Kindness is contagious

Explore a trove of insights in this engaging article, celebrated within our welcoming DEV Community. Developers from every background are invited to join and enhance our shared wisdom.

A genuine "thank you" can truly uplift someone’s day. Feel free to express your gratitude in the comments below!

On DEV, our collective exchange of knowledge lightens the road ahead and strengthens our community bonds. Found something valuable here? A small thank you to the author can make a big difference.

Okay