Back to: ASP.NET Core Tutorials For Beginners and Professionals
Fluent API Async Validators in ASP.NET Core MVC:
In this article, I will discuss Fluent API Async Validator in ASP.NET Core MVC Applications with Examples. Please read our previous article discussing the Different Types of Fluent API Validation Examples in ASP.NET Core MVC Applications.
Fluent API Async Validators in ASP.NET Core MVC:
You can use FluentValidation’s MustAsync and CustomAsync Validator methods if you need to perform asynchronous validations. These two validator methods allow the inclusion of asynchronous code, which is particularly useful when performing IO-bound operations, such as retrieving data from a database and invoking third-party API, as part of the validation process.
- MustAsync(): It Defines an asynchronous predicate validator on the current rule builder using a lambda expression to specify the predicate. Validation will fail if the specified lambda returns false. Validation will succeed if the specified lambda returns true.
- CustomAsync(): Defines a custom validation rule that asynchronously validates using custom logic.
Let’s understand how to use these Fluent API Asynchronous Validators in an ASP.NET Core MVC application.
Define the Model:
Let us consider a simple UserRegistration model where we want to ensure that the provided email address isn’t already registered. So, create a class file named UserRegistration.cs and copy and paste the following code.
namespace FluentAPIDemo.Models { public class UserRegistration { public string Email { get; set; } public string Password { get; set; } } }
Service
We need to create a service to Check Whether the Email ID Exists or not in the Database. So, create a class file named UserService.cs and copy and paste the following code. As you can see in the following code, the EmailExistsAsync method is used to check if an email is registered.
using FluentValidation; namespace FluentAPIDemo.Models { public interface IUserService { Task<bool> EmailExistsAsync(string email); } public class UserService : IUserService { public Task<bool> EmailExistsAsync(string email) { List<string> emails = new List<string>() { "user1@example.com", "user2@example.com", "user2@example.com", "user4@example.com" }; if(emails.Contains(email)) { return Task.FromResult(true); } else { return Task.FromResult(false); } } } }
Set Up the Validator:
Now, let’s define a validator for the UserRegistration model. So, create a class file named UserRegistrationValidator.cs and copy and paste the following code. As you can see in the following code, we have used the MustAsync and CustomAsync validator methods to write the custom logic, invoking the EmailExistsAsync method to validate whether the Email is already registered.
using FluentValidation; namespace FluentAPIDemo.Models { public class UserRegistrationValidator : AbstractValidator<UserRegistration> { //private readonly IUserService _userService; public UserRegistrationValidator(IUserService userService) { // Synchronous validation RuleFor(x => x.Password) .NotEmpty() .WithMessage("Password is required."); // Asynchronous validation // MustAsync //RuleFor(x => x.Email) // .NotEmpty() // .EmailAddress() // .MustAsync(async (email, cancellation) => // !await userService.EmailExistsAsync(email)) // .WithMessage("Email is already registered."); // Asynchronous validation // CustomAsync RuleFor(p => p.Email) .CustomAsync(async (email, context, cancellation) => { bool isUnique = await userService.EmailExistsAsync(email); if (isUnique) { context.AddFailure($"Email: {email} is already registered"); } }); } } }
Here’s a brief explanation:
- MustAsync: This method is also used for asynchronous operations. The provided predicate should return true if validation succeeds, and false if it fails.
- CustomAsync: The CustomAsync Validator method provides greater flexibility than MustAsync, allowing for custom failure messages based on complex asynchronous logic.
Registering the Validator:
Add the following code with the Program class to register Fluent API Validation and the Model and corresponding validator. You must disable Auto validation when working with the async validator, or you will get a run time exception.
//Enables integration between FluentValidation and ASP.NET MVC's automatic validation pipeline. //builder.Services.AddFluentValidationAutoValidation(); //Enables integration between FluentValidation and ASP.NET client-side validation. builder.Services.AddFluentValidationClientsideAdapters(); //Registering Model and Validator to show the error message on the client side builder.Services.AddTransient<IValidator<UserRegistration>, UserRegistrationValidator>(); //Registering the UserService builder.Services.AddTransient<IUserService, UserService>();
Using the Validator in ASP.NET Core MVC Actions:
Modify the Home Controller as follows. Since we’re using asynchronous validation, ensure that the ASP.NET Core MVC Controller action is also asynchronous. With the async validator in place, Auto Validation will not work, so we need to create an instance of UserRegistrationValidator and invoke the ValidateAsync method by passing the UserRegistration object as a parameter that needs to be validated.
using FluentAPIDemo.Models; using Microsoft.AspNetCore.Mvc; namespace FluentAPIDemo.Controllers { public class HomeController : Controller { private readonly IUserService _userService; public HomeController(IUserService userService) { _userService = userService; } public IActionResult Register() { return View(); } [HttpPost] public async Task<IActionResult> Register(UserRegistration registration) { var validationResult = await new UserRegistrationValidator(_userService).ValidateAsync(registration); if (!validationResult.IsValid) { foreach (var error in validationResult.Errors) { ModelState.AddModelError(error.PropertyName, error.ErrorMessage); } return View(registration); // Return with validation errors. } // Continue with the registration process... return RedirectToAction("Success"); } public string Success() { return "Registration Successful"; } } }
Displaying Errors in Views:
Now, we need to display the validator error inside a view. We can use the tag helpers to bind and display model validation errors here. So, add Register.cshtml view and copy and paste the following code.
@model FluentAPIDemo.Models.UserRegistration @{ ViewData["Title"] = "Register"; } <h1>User Register</h1> <hr /> <div class="row"> <div class="col-md-4"> <form asp-action="Register" asp-controller="Home" method="post"> <div asp-validation-summary="All" class="text-danger"></div> <div class="form-group"> <label asp-for="Email" class="control-label"></label> <input asp-for="Email" class="form-control" /> <span asp-validation-for="Email" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Password" class="control-label"></label> <input asp-for="Password" class="form-control" /> <span asp-validation-for="Password" class="text-danger"></span> </div> <div class="form-group"> <input type="submit" value="Register" class="btn btn-primary" /> </div> </form> </div> </div>
The most important point that you need to remember is to use the MustAsync and CustomAsync Fluent API Validators only when necessary. Performing too many I/O-bound operations during validation can slow your application. Secondly, with MustAsync and CustomAsync Fluent API Validators, ASP.NET Core MVC Auto Validation will not work and in that case, we need to do the validation manually by calling the ValidateAsync method.
In the next article, I will discuss Fluent API Custom Validators in ASP.NET Core MVC Applications with Examples. In this article, I try to explain Fluent API Async Validators in ASP.NET Core MVC Applications with Examples. I hope you enjoy this article.
About the Author: Pranaya Rout
Pranaya Rout has published more than 3,000 articles in his 11-year career. Pranaya Rout has very good experience with Microsoft Technologies, Including C#, VB, ASP.NET MVC, ASP.NET Web API, EF, EF Core, ADO.NET, LINQ, SQL Server, MYSQL, Oracle, ASP.NET Core, Cloud Computing, Microservices, Design Patterns and still learning new technologies.