Telerik blogs
DotNetT2 Dark_1200x303

In this article, we’ll learn how to use the FluentValidation library and create simple and strongly typed validations.

If you work with the C# language or are preparing for it, something that will always accompany you is the validation of objects and their properties. But this can be simple and easy, as long as you use the right tools.

What Is FluentValidation?

FluentValidation is a very popular library created with the purpose of making data validations simple and fast.

Through it, we can use Lambda expressions to build validation rules and return error messages for each invalid object or property.

Hands-on Approach

To use FluentValidation in practice, we will create a console application in .NET 5 and install the FluentValidation library.

You can access the complete project code at this link: FluentValidation App Source Code.

First, open your favorite C# IDE. In this example, I will use Visual Studio 2019. Click on “Create a new project” --> “Console Application” --> Write the name of the app (ValidationExampleApp) --> .NET 5 --> Create.

In the project, create a folder called “Models,” and inside it create a class called “Product” and replace its code with this:

using System;
using System.Collections.Generic;

namespace ValidationExampleApp.Models
{
    public class Product
    {
        public Product(IEnumerable<Item> products)
        {
            this.Products = products;
        }

        public IEnumerable<Item> Products { get; set; }
    }

    public class Item
    {
        public Item(int id, string name, string seller, string catRefId, decimal quantity)
        {
            this.Id = id;
            this.Name = name;
            this.Seller = seller;
            this.CatRefId = catRefId;
            this.Quantity = quantity;
        }

        public int Id { get; set; }
        public string Name { get; set; }
        public string Seller { get; set; }
        public string CatRefId { get; set; }
        public decimal Quantity { get; set; }
    }
}

Now, download the FluentValidation library. You can use NuGet Package Manager in Visual Studio—the version used in this example is 10.3.3.

In this scenario we will have an entity called Product that will receive a list of Items. Our objective will be to validate the properties of this object as they are being filled in.

First, let’s create a static class where the error messages will be if entries in the fields are invalid.

Then, inside the project create a folder called “Errors,” and inside that, a class called “ProductErrors.”

Open the class and replace your code with this:

namespace ValidationExampleApp.Errors
{
    public static class ProductErrors
    {
        public static string IdErrorMessage { get; set; } = "Id field cannot be null or empty.";
        public static string ProductErrorMessage { get; set; } = "Product field cannot be null or empty.";
        public static string NameErrorMessage { get; set; } = "Name field cannot be null or empty.";
        public static string SellerErrorMessage { get; set; } = "Seller field cannot be null or empty.";
        public static string CatRefIdErrorMessage { get; set; } = "CatRefId field cannot be null or empty.";
        public static string QuantityErrorMessage { get; set; } = "Quantity field must be greater than 0.";
    }
}

Now we can create the validation class. It will inherit from the class “AbstractValidator<T>” which will receive as a parameter the class to be validated—in this case, “Product”.

To do this, within the project create a new folder called “Validators,” and within it create a class called “ProductValidator.”

Then, inside it, paste the following code:

using FluentValidation;
using System.Linq;
using ValidationExampleApp.Errors;
using ValidationExampleApp.Models;

namespace ValidationExampleApp.Validators
{
    public class ProductValidator : AbstractValidator<Product>
    {
        public ProductValidator()
        {
            RuleFor(x => x.Products)
                .NotNull()
                .WithMessage(ProductErrors.ProductErrorMessage)
                .Must(x => x.Any())
                .WithMessage(ProductErrors.ProductErrorMessage);

RuleFor(x => x.Products.Select(p => p.Id))
.Must(x => !x.Any(s => s <= 0))
            	.WithMessage(ProductErrors.IdErrorMessage);

            RuleFor(x => x.Products.Select(p => p.Seller))
               .Must(n => !n.Any(s => string.IsNullOrEmpty(s) || string.IsNullOrWhiteSpace(s)))
               .WithMessage(ProductErrors.SellerErrorMessage);
            
            RuleFor(x => x.Products.Select(p => p.Name))
                .Must(x => !x.Any(s => string.IsNullOrEmpty(s) || string.IsNullOrWhiteSpace(s)))
                .WithMessage(ProductErrors.NameErrorMessage);

            RuleFor(x => x.Products.Select(p => p.CatRefId))
                .Must(x => !x.Any(s => string.IsNullOrEmpty(s) || string.IsNullOrWhiteSpace(s)))
                .WithMessage(ProductErrors.CatRefIdErrorMessage);

            RuleFor(x => x.Products.Select(p => p.Quantity))
               .Must(x => !x.Any(s => s <= 0))
               .WithMessage(ProductErrors.QuantityErrorMessage);
        }
    }
}

In the code above, the class “ProductValidator” contains within its constructor some methods necessary to validate the entity “Product” and its properties. With FluentValidation, we can check if properties are empty or null, and also if numeric fields are greater than zero.

Now let’s create the methods responsible for implementing Product entity validation.

So in the Program file, replace the existing code with the code below—don’t worry, soon we’ll see the explanation of each of the methods.

using FluentValidation.Results;
using FluentValidationExample.Models;
using FluentValidationExample.Validators;
using System;
using System.Collections.Generic;
using System.Linq;

namespace FluentValidationExample
{
    public class Program
    {
        public static void Main(string[] args)
        {
            var product = FillProductReady();

            var resultValidator = ValidateProduct(product);

            if (!resultValidator.IsValid)
            {
                Console.WriteLine("The following errors ocurred: ");
                resultValidator.Errors.ToList().ForEach(x => PrintErrorMessages(x.ErrorMessage));
            }
        }

        static Product FillProductReady()
        {
            var item = new Item(0, null, null, null, 0);
            var listItem = new List<Item>();
            listItem.Add(item);
            return new Product(listItem);
        }

        static ValidationResult ValidateProduct(Product product) =>
            new ProductValidator().Validate(product);

        static void PrintErrorMessages(string errorMessage) =>
            Console.WriteLine($"* {errorMessage}");
    }
}

First, we created a method called “FillProductReady” which is responsible for filling the Product entity with values. Our goal is to validate these fields, so we are defining the properties with null values and assigning their return to a variable “var product.”

Afterward, we create a method called “ValidateProduct” that will implement the “Validate” method from the FluentValidation library, will receive an instance of our Product variable, and will return an object of the “ValidationResult” type.

With the validation result, we can know if the validated object is valid or not. If it is invalid, we will print an error message informing the user which fields are wrong.

If you followed all the previous steps, you can run the application and find the following output in the console.

Debug Console showing a list of errors with Id, Seller, Name, CatRefId, Quantity fields and why there is an error

Our object was validated and, as it did not pass the validation rules that we implemented in the “ProductValidator” class, the error messages corresponding to each invalid field were displayed. So, we already have a simple way to validate objects without a lot of “if/else,” thanks to FluentValidation.

Conclusion

This was a demonstration of using the FluentValidation library to implement validation rules. There are also other ways to do this—the important thing is to use what best fits your needs.


C#
assis-zang-bio
About the Author

Assis Zang

Assis Zang is a software developer from Brazil, developing in the .NET platform since 2017. In his free time, he enjoys playing video games and reading good books. You can follow him at: LinkedIn and Github.

Related Posts

Comments

Comments are disabled in preview mode.