Data annotation is not enough for you and you looking for another framework to building validation rules.

What Is FluentValidate?

A small validation library for .NET that uses a fluent interface and lambda expressions for building validation rules (written by Jeremy Skinner)

For example, let's say we had this view model object:

​​public class RegisterViewModel
{
    public int Id { get; set; }

    [Display(Name = "Email")]
    public string Email { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    public string ConfirmPassword { get; set; }
}

When we wanna validate this model we use Data Annotation like this below:

​public class RegisterViewModel
{
    public int Id { get; set; }

    [Required]
    [DataType(DataType.EmailAddress)]
    [Display(Name = "Email")]
    public string Email { get; set; }
 
    [Required]
    [StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }
 
    [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    public string ConfirmPassword { get; set; }
}

This model just have 3 properties, what going on if we have a big model equal to 40 properties or bigger? It looks too verbose to me.

I need a single point for data validation. If you are like me, Fluent Validate will be your, let change this model using FluentValidate.

Setup FluentValidate

Firstly, you need to download FluentValidate from NuGet by command.

​PM> Install-Package FluentValidation -Version 6.2.0

Secondly, create Validator for your model. In the above case, I create RegisterViewModelValidator.

​using FluentValidation;

namespace FluentValidationDemo.Models
{
    public class RegisterViewModelValidator : AbstractValidator<RegisterViewModel>
    {
        RuleFor(x => x.Email)
            .NotNull().WithMessage("Required")
            .EmailAddress().WithMessage("Invalid email");

        RuleFor(x => x.Password)
            .NotNull().WithMessage("Required")
            .Length(6, 100).WithMessage("Too short or too long");

        RuleFor(x => x.ConfirmPassword)
            .NotNull().WithMessage("Required")
            .Equal(x => x.Password).WithMessage("Not matched");

        Custom(x =>
        {
            // Custom validate, do some business logic for validation, ex: validate with database
            FluentValidateSampleEntities dbContext = new FluentValidateSampleEntities();
            bool isExistUser = dbContext.Users.Any(y => y.Email.ToLower() == x.Email.ToLower());
            return isExistUser ? new ValidationFailure(nameof(x.Email), "This email is already registered") : null;
        });
    }
}

Then just call Validate method from validator to validate your model.

​RegisterViewModel register = new RegisterViewModel
{
    Email = "email",
    Password = "password",
    ConfirmPassword = "password not match"
};

RegisterViewModelValidator validator = new RegisterViewModelValidator();
ValidationResult results = validator.Validate(register);

bool validationSucceeded = results.IsValid;
IList<ValidationFailure> failures = results.Errors;

and you will get the result of validation in a normal case

FluentValidation Debug

and the result of validation with database case

FluentValidation Debug with database

Easy enough, right?

You can download my sample project by this link.

If you develop an MVC 5 or WEB API, FluentValidate already supports for it. Let I show you how to setup.

FluentValidate with MVC 5

Firstly, you need to download FluentValidate MVC 5 from NuGet by command.

​PM> Install-Package FluentValidation.MVC5​

Secondly, configure FluentValidate for Controller in Application Start.

​protected void Application_Start(object sender, EventArgs e)
{
    FilterConfig.Configure(GlobalFilters.Filters);
    RouteConfig.Configure(RouteTable.Routes);
    ...
    FluentValidationModelValidatorProvider.Configure();
}

Finally, you just put Validator as an attribute in your model.

​[Validator(typeof(RegisterViewModelValidator))]
public class RegisterViewModel
{
    public int Id { get; set; }

    [Display(Name = "Email")]
    public string Email { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    public string ConfirmPassword { get; set; }
}​

Then your ModelState.IsValid will work perfectly like using Data Annotation Validate.

FluentValidate MVC 5 also support validate in client site with jquery.validate.unobtrusive. In the view, you just using Razor for generating HTML (remember put Html.ValidationMessageFor for model's property, if not the jquery validate unobtrusive will not working).

So I have a view below.

​@model FluentValidationSample.Models.RegisterViewModel
@{
    ViewBag.Title = "Home | Register";
}
@section scripts{
    <!-- Remember to include jquery validate and unobtrusive -->
    <script src="~/Scripts/jquery.validate.js"></script>
    <script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
}
@using (Html.BeginForm("Register", "Home", FormMethod.Post))
{
    @Html.HiddenFor(m => m.Id)
    <div>
        @Html.LabelFor(m => m.Email)
        <div>
            @Html.TextBoxFor(m => m.Email)
            @Html.ValidationMessageFor(m => m.Email)
        </div>
    </div>
    <div>
        @Html.LabelFor(m => m.Password)
        <div>
            @Html.PasswordFor(m => m.Password)
            @Html.ValidationMessageFor(m => m.Password)
        </div>
    </div>
    <div>
        @Html.LabelFor(m => m.ConfirmPassword)
        <div>
            @Html.PasswordFor(m => m.ConfirmPassword)
            @Html.ValidationMessageFor(m => m.ConfirmPassword)
        </div>
    </div>
    <div>
        <div>
            <input type="submit" name="Submit" />
        </div>
    </div>
}


Then in runtime, it works well and shows validation like this

FluentValidation Result

If you wanna make more User Experience by jQuery ajax post data from the client to the server for real time validate with the database, you can use Remote Attribute of jquery.validate.unobtrusive on the server side to generate it.

You need to change the RegisterViewModel like this below

​[Validator(typeof(RegisterViewModelValidator))]
public class RegisterViewModel
{
    public int Id { get; set; }
   
    // Use AdditionalFields if you wanna to pass more data to JsonResult, ex: Id
    [Remote("IsNotExistEmail", "Home", HttpMethod = "POST", AdditionalFields = "Id", ErrorMessage = @"This email is already registered")]
    [Display(Name = "Email")]
    public string Email { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Password")]
    public string Password { get; set; }

    [DataType(DataType.Password)]
    [Display(Name = "Confirm password")]
    public string ConfirmPassword { get; set; }
}

Then you need to create JsonResult "IsNotExistEmail" in controller "Home" to do the validation

​[HttpPost]
public JsonResult IsNotExistEmail(string email, int id)
{
    FluentValidateSampleEntities dbContext = new FluentValidateSampleEntities();
    bool isNotExistUser = !dbContext.Users.Any(y => y.Email.ToLower() == email.ToLower() && y.Id != id);

    // Return true is valid data and jquery validate will pass it
    // false is not valid data, the form will stuck when submit
    return Json(isNotExistUser, JsonRequestBehavior.AllowGet);
}

Remember to put the Id in the view and create instance of ViewModel for view

<!--
    Put this in view, 
    if you don't have this the JsonResult validation in Controller will
    throw 500 because the value of Id is null intead of integer
-->
@Html.HiddenFor(m => m.Id)

// In ActionResult return view need to create instance for viewmodel
public ActionResult Index()
{
    return View(new RegisterViewModel());
}

An Important NuGet Note

Fluent Validation

​PM> Install-Package FluentValidation -Version 6.2.0

Fluent Validate for MVC5

​PM> Install-Package FluentValidation.MVC5

Fluent Validate for API

​PM> Install-Package FluentValidation.WebAPI

Github link

Summary

Fluent Validate, it gives me far better control of my validation rules.

It has excellent client side validation support for most standard validation rules.

Data Annotation attributes can be tested to ensure they're there.

It separates the validation from my view models.

For small systems where all the validation logic can be handled using annotations, I would recommend just using annotation, because they're so easy to set up.

For larger, more complex systems, I would recommend separating the validation concern using validator objects.

Happy Coding!