One of my team's favorite NuGet packages is FluentValidation, a package that allows us to extend the validation rules provided by System.ComponentModel to give us more flexible validation framework.
In this post, we'll go over how to set up and use FluentValidation in a simple MVC web application. Let's get started!
The Sample Project
Here's the sample project on GitHub for this post:
Getting the FluentValidation Package
First things first, we need to grab FluentValidation from NuGet. Because we want to use it in an MVC project, we need the MVC extensions package as well. For MVC up to 5.1, grab the FluentValidation-MVC4 package from NuGet, either via the NuGet Package Manager interface or in the package manager console:
pm> install-package FluentValidation.MVC4
Once that's installed, we can start setting up our validation.
Example Validation
Here's a sample class that uses System.ComponentModel validation:
public class AddMovieVM
{
[Required(ErrorMessage = "Please enter a title.")]
[Display(Name = "Title:")]
[StringLength(100, ErrorMessage = "The title must be less than 100 characters.")]
public string Title { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Release Date:")]
[Required(ErrorMessage = "Please enter a release date.")]
public DateTime ReleaseDate { get; set; }
[Display(Name = "Running Time:")]
[Required(ErrorMessage = "Please enter the running time.")]
[Range(0, int.MaxValue, ErrorMessage = "The Running Time cannot be negative")]
public int RunningTimeMinutes { get; set; }
[Display(Name = "Leading Actor:")]
[Required(ErrorMessage = "Please enter the leading actor.")]
public string LeadingActor { get; set; }
[Display(Name = "Supporting Actor:")]
public string SupportingActor { get; set; }
}
The validation rules here are not too difficult: only SupportingActor
can be null, RunningTimeMinutes
must not be negative, etc. There's one rule I want to implement but can't, using this setup: Leading and Supporting actors must not be the same person.
Let's use FluentValidation to implement that extra rule, as well as implement all the rules we already have defined.
Setting up FluentValidation
The first thing we need to do is add a line to the Global.asax file's Application_Start()
method:
protected void Application_Start()
{
AreaRegistration.RegisterAllAreas();
FilterConfig.RegisterGlobalFilters(GlobalFilters.Filters);
RouteConfig.RegisterRoutes(RouteTable.Routes);
BundleConfig.RegisterBundles(BundleTable.Bundles);
FluentValidationModelValidatorProvider.Configure();
}
Now we can start setting up the actual validation. FluentValidation uses AbstractValidator
, a generic class that allow us to hook into existing model classes and create rules. Let's create a new class called AddMovieVMValidator
:
public class AddMovieVMValidator : AbstractValidator<AddMovieVM>
{
public AddMovieVMValidator() { }
}
Notice the inheritance from AbstractValidator
. That inheritance is what allows us to associate the validator class with the source class and consequently use lambda queries when setting up our validation rules.
Speaking of adding rules, let's add the first rule: Title must not be null.
public class AddMovieVMValidator : AbstractValidator<AddMovieVM>
{
public AddMovieVMValidator()
{
RuleFor(x => x.Title)
.NotNull()
.WithMessage("Please enter a title");
}
}
The NotNull()
method specifies that the property cannot be null, just like RequiredAttribute does, and the WithMessage()
method sets an error message to be displayed if the validation of that property fails.
The rule for ReleaseDate looks similar:
public class AddMovieVMValidator : AbstractValidator<AddMovieVM>
{
public AddMovieVMValidator()
{
RuleFor(x => x.Title)
.NotNull()
.WithMessage("Please enter a title");
RuleFor(x => x.ReleaseDate)
.NotNull()
.WithMessage("Please enter a release date");
}
}
Now what about the rules for the RunningTimeMinutes
property? We need two: that it cannot be null
, and that it cannot be negative. FluentValidation allows us to chain rules that operate against the same property, like so:
public class AddMovieVMValidator : AbstractValidator<AddMovieVM>
{
public AddMovieVMValidator()
{
RuleFor(x => x.Title)
.NotNull()
.WithMessage("Please enter a title");
RuleFor(x => x.ReleaseDate)
.NotNull()
.WithMessage("Please enter a release date");
RuleFor(x => x.RunningTimeMinutes)
.NotNull()
.WithMessage("Please enter the running time")
.GreaterThan(0)
.WithMessage("The Running Time cannot be negative");
}
}
Finally, we come to the LeadingActor
property. This property cannot be null
, but it also cannot be the same as SupportingActor
. For this rule, FluentValidation provides us with the NotEqual()
operator, like so:
public class AddMovieVMValidator : AbstractValidator<AddMovieVM>
{
public AddMovieVMValidator()
{
RuleFor(x => x.Title)
.NotNull()
.WithMessage("Please enter a title");
RuleFor(x => x.ReleaseDate)
.NotNull()
.WithMessage("Please enter a release date");
RuleFor(x => x.RunningTimeMinutes)
.NotNull()
.WithMessage("Please enter the running time")
.GreaterThan(0)
.WithMessage("The Running Time cannot be negative");
RuleFor(x => x.LeadingActor)
.NotNull()
.WithMessage("Please enter the leading actor")
.NotEqual(x => x.SupportingActor)
.WithMessage("The leading and supporting actors cannot be the same person");
}
}
As you can see, FluentValidation provides a much more flexible way of validating inputs. You can even use methods like Exists()
, ExclusiveBetween()
, or CreditCard()
to perform more complex validation.
There's one last thing we have to do before we can use this class: we have to decorate the class with the Validator
attribute:
[Validator(typeof(AddMovieVMValidator))]
public class AddMovieVM
{
[Display(Name = "Title:")]
public string Title { get; set; }
[DataType(DataType.Date)]
[Display(Name = "Release Date:")]
public DateTime ReleaseDate { get; set; }
[Display(Name = "Running Time:")]
public int RunningTimeMinutes { get; set; }
[Display(Name = "Leading Actor:")]
public string LeadingActor { get; set; }
[Display(Name = "Supporting Actor:")]
public string SupportingActor { get; set; }
}
And now we have integrated FluentValidation.
Why use it?
The primary reason my group uses it is that it provides a validation framework that allows features the regular ComponentModel framework doesn't have.
What's probably not obvious from the simple example I gave above is that FluentValidation is extremely testable, and it's very easy to set up unit tests for it. Jerrie Pelser has a nice set of blog posts describing how to do this.
As always, I have a sample project on GitHub that has both validation using System.ComponentModel and FluentValidation. Feel free to grab it and try it out! The documentation for FluentValidation over on its own site is also really thorough, so check that out as well.
Happy Coding!