Fluent Validation Dependency Injection .Net

Bob Code
3 min readJul 5, 2024

--

This example demonstrates how to use Fluent Validation with Dependency Injection in a .NET application to validate objects before performing operations on them.

https://levelup.gitconnected.com/how-to-use-fluentvalidation-in-asp-net-core-net-6-543d52bd36b4

Why use Fluent Validation?

  • Clear and maintainable validation logic
  • Loose coupling between validators and consumer classes
  • Isolated validation logic for each request (Transient scope)

Interface

The IClubCollectionValidators interface defines two properties for validators

public interface IClubCollectionValidators
{
IValidator<ClubCollection> ClubCollectionNameValidator { get; }
IValidator<Header> HeaderValidator { get; }
}

ClubCollectionNameValidator and HeaderValidator are properties that return validators for ClubCollection and Header objects, respectively.

Implementation

The ClubCollectionValidators class implements the IClubCollectionValidators interface

public class ClubCollectionValidators : IClubCollectionValidators
{
public IValidator<ClubCollection> ClubCollectionNameValidator { get; }
public IValidator<Header> HeaderValidator { get; }

public ClubCollectionValidators()
{
ClubCollectionNameValidator = new InternalClubCollectionNameValidator();
HeaderValidator = new InternalHeaderValidator();
}

private class InternalClubCollectionNameValidator : AbstractValidator<ClubCollection>
{
public InternalClubCollectionNameValidator()
{
RuleFor(x => x.ClubCollectionName).NotEmpty().WithMessage("Club Collection Name is required.");
RuleFor(x => x.ClubSalesforceID).NotEmpty().WithMessage("Club Salesforce ID is required.");
RuleFor(x => x.CountryCode).NotEmpty().WithMessage("Country Code is required.");
}
}

private class InternalHeaderValidator : AbstractValidator<Header>
{
public InternalHeaderValidator()
{
RuleFor(x => x.HeaderName).NotEmpty().WithMessage("HeaderName is required.");
RuleFor(x => x.Order).NotEmpty().WithMessage("Order is required.");
RuleFor(x => x.Order).InclusiveBetween(0, 100).WithMessage("Order must be between 0 and 100.");
}
}
}

ClubCollectionValidators contains two validators: InternalClubCollectionNameValidator and InternalHeaderValidator.

InternalClubCollectionNameValidator validates that ClubCollectionName, ClubSalesforceID, and CountryCode are not empty.

InternalHeaderValidator validates that HeaderName is not empty, and Order is between 0 and 100.

Dependency Injection

To use these validators in the application, they are registered with the Dependency Injection (DI) container:

services.AddTransient<IClubCollectionValidators, ClubCollectionValidators>();

This registration adds ClubCollectionValidators as a transient service, allowing it to be injected wherever IClubCollectionValidators is required.

Why Transient? This creates a new instance of the validator for each request. This is the safest option as it ensures isolated validation logic for each operation. It’s particularly important if your validators have dependencies that shouldn’t be shared across requests.

While Transient is generally recommended, Scoped or Singleton could be considered in specific scenarios (e.g., if validators have state or expensive dependencies).

Using it

Constructor

An example service (CollectionsService) demonstrates how to use the injected validators

public class CollectionsService : ICollectionsService
{

private readonly IClubCollectionValidators _clubCollectionValidators;

public CollectionsService(IClubCollectionValidators clubCollectionValidators)
{

_clubCollectionValidators = clubCollectionValidators;
}

Validation

public async Task AddHeaderToClubCollection(Header header)
{
var validation = await _clubCollectionValidators.HeaderValidator.ValidateAsync(header);
if (!validation.IsValid)
{
throw new ValidationException(validation.Errors);
}
try
{
await _collectionsAPI.AddHeader(header.ClubCollectionSalesforceID, header.HeaderName, header.Order);
}
catch (Exception ex)
{
_logger.LogError(ex, $"Error adding header to Club Collection with name {header.HeaderName}");
throw;
}
}

The CollectionsService constructor injects IClubCollectionValidators.

In AddHeaderToClubCollection, the HeaderValidator validates the header object.

If validation fails, a ValidationException is thrown with the validation errors.

If validation passes, the header is added using _collectionsAPI.

This setup ensures that objects are validated consistently and thoroughly before any operations are performed, leveraging the power of Fluent Validation within a DI framework.

Further reading

For a more comprehensive understanding of Fluent Validation, refer to the official documentation or explore the GitHub repository mentioned in the sources.

In your application/ business layer, on top of validation, you can also use AutoMapper and MediatR

Sources

--

--

Bob Code

All things related to Memes, .Net/C#, Azure, DevOps and Microservices