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

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Discover a treasure trove of wisdom within this insightful piece, highly respected in the nurturing DEV Community enviroment. Developers, whether novice or expert, are encouraged to participate and add to our shared knowledge basin.

A simple "thank you" can illuminate someone's day. Express your appreciation in the comments section!

On DEV, sharing ideas smoothens our journey and strengthens our community ties. Learn something useful? Offering a quick thanks to the author is deeply appreciated.

Okay