Fluent Validation in ASP.NET Core

Fluent Validation in ASP.NET Core

One of the ways used to validate models, is the use of data annotations. But then there are quite some issues with this approach for a scalable system. The good thing is that there is a library, that can help enhance validation, giving you total control.

The Problem

Data validation is extremely vital for any application. As a .NET developer, the GO-TO approach for model validation in any .NET application is data annotations, where you have to declare attributes over the property of models.


public class User
  {
   [Required]
   public string FirstName { get; set; }
   [Required]
   public string LastName { get; set; }
   [EmailAddress]
   public string Email { get; set; }
}

It is fine to work with data annotations, but the problem with the implementation of data annotation is that there will be a lot of duplication lines throughout your application. So what is the solution?

The Solution -> FluentValidation

As humans, we always prefer having whatever we do to be clean, easy to create and maintain. This is what fluent validation offers . Fluent validation is a free to use .NET validation library that helps you make your validations clean, easy to create and also maintain.

A few things make fluent validation standout, one of them is that you can work on external models that you do not have access to, with ease. With the fluent validation library, you can separate the model classes from the validation logic like it is supposed to be. It also helps in better control of validations. Fluent validation uses lambda expressions to build validation rules.

Features of FluentValidation

  • Rules can be chained together in a fluent way, allowing you to build up complex validation logic in a readable and maintainable manner. Fluent validation allows you to chain together multiple validation rules for a single property in a fluent way, using a set of rule builders that are provided by the library. For example , here is how you might use fluent validation to define a set of validation rules for a UserValidator class:

      public class UserValidator : AbstractValidator<UserData>{
          public UserValidator(){
            RuleFor(x => x.FirstName).NotEmpty().MinLength(2).MaximumLength(50)
            RuleFor(x => x.Phonenumber).NotNull();
            RuleFor(x => x.Password).MinimumLength(8);
            RuleFor(x => x.EmailAddress).EmailAddress();
            RuleFor(x => x.Age).GreaterThan(0).LessThan(150);
    
              }
          }
    

    In this example, the RuleFor() method is used to specify a property that the validation rules apply to, examples are FirstName, Phonenumber, Password, EmailAddress and Age. It also involves a series of validation rules which are chained together using method calls. For example, the NotEmpty(), MinimumLength(), and MaximumLength() methods are used to specify that the FirstName property must not be empty, and must have a minimum and maximum length.

    This fluent syntax makes it easy to read and understand the validation logic, and allows you to build up complex validation rules in a readable and maintainable way.

  • Fluent Validation supports asynchronous validation logic, which means that you can use asynchronous methods within your rules. This can be useful if you need to perform some time consuming operation (Such as making a network request) as part of your validation logic. To define an asynchronous validation rule, you can use the Async() method provided by Fluent Validation. Here is an example of how you can use the Async() method to define an asynchronous validation rule.

RuleFor(x => x.Email).MustAsync(async(email, cancellationToken) => 
{ var isEmailUnique = await CheckEmailIsUniqueAsync(email,cancellationToken);
return isEmailUnique;
 });

In this example , the CheckEmailIsUniqueAsync() method is an asynchronous method that checks whether a given email address is unique. The MustAsync() method is used to specify that the validation rule should be asynchronous, and the lambda expression passed to the method defines the asynchronous validation logic.

You can use the Async method with any of the built-in validators provided by fluent validation, as well as with custom validators that you create.

  • Rules can be chained together in a fluent way, allowing you to build up complex validation logic in a readable and maintainable manner. One of the key benefits of using fluent validation is the ability to chain validation rules together in a way that reads like a sentence and is easy to understand. This allows you to build up complex validation logic in a clear and maintainable way. For example, you could use fluent validation to create a rule that ensures that a string property called "Username" is not empty, has a minimum length of 6 characters, and only contains alphanumeric characters. The validation rule might look something like this:

      RuleFor(x => x.UserName).NotEmpty().WithMessage(“Username must be at least 6 characters long”).Matches(“^[a-zA-Z0-9]*$”).
      WithMessage(“Username can only contain letters and numbers.”);
    

In this example, the three validation rules are chained together using the fluent API, making it easy to understand what the rule is checking for. This can help make your validation logic more maintainable in the long run, as it is clear and easy to understand.

  • Fluent Validation can easily be extended to support additional validation scenarios or to integrate with other libraries or frameworks. It is designed to be flexible and extensible, which makes it easy to support additional validation scenarios or to integrate with other libraries or frameworks. For example, to support additional validation scenarios, you can create custom validators that implement your own validation logic. These custom validators can be easily integrated into your validation rules using the FluentValidation's RuleFor and Must methods.

  • FluentValidation also provides hooks that allow you to customize various aspects of the validation process, such as error message formatting, localization, and property display names. This makes it easy to adapt FluentValidation to different cultures and conventions.

  • Additionally, FluentValidation can be easily integrated with other libraries and frameworks like ASP.NET Core, MVC, and Web API. FluentValidation provides built-in support for these frameworks, which makes it easy to use FluentValidation for server-side validation in web applications. Because of its flexibilty, FluentValidation can be a great option for organizations that want a consistent and centralized approach to validation across their applications, in this way it will seperate the validation logic from other functionality of the application and make it easy to maintain and scale the application.

  • Fluent validation is designed to have good performance and be scalable. The library uses a compiled expression tree approach to build validation rules, which means that the validation logic is compiled into a fast-executing delegate at runtime. This results in faster validation times compared to reflection-based approaches. Additionally, Fluent Validation is designed to be used in a multi-threaded environment and is thread-safe. You can use the .AsParallel() method on a validator to run validation rules in parallel, which can improve performance on multi-core systems.

    Finally, Fluent validation has been designed to be extensible and flexible, allowing you to tailor it to your specific validation needs without sacrificing performance or scalability.