Taking Your Game to the Next Level: FluentValidation
This is the last in a three-part series on validation techniques in MVC 3. See the first post and the second post to get the solution set up.
Try this experiment: make sure your Global.asax Application_Start or database context does not have a database SetInitializer method exposed. That's the one that drops and recreates the database if the model changes. Ok, now go into your Widget model class and remove an attribute. Maybe one of the [Required] attributes above Name or Color. Now build and run the app and go to the Widget list. Yep, the app throws an InvalidOperationException with the error message "The model backing the 'WidgetApplicationContext' context has changed since the database was created." Oops, that would mean your database gets wiped out and you have to re-run your populate scripts. Nice huh?
So why do we care? We'll have our properties decorated at design time and we won't mess with them. That's true for the obvious ones like GreaterThan, Length, or Required. But what about the custom attributes? NameAvailableAttribute is required today but it might not be tomorrow. We might have other business rules we want to implement as we go along and what a hassle it would be add and remove attributes, thus breaking our EF model. Wouldn't it be cool to decouple as much as possible the validators and business rules from the model class? There are several good ways to do it but I want to illustrate FluentValidation written by Jeremy Skinner because I think it's one of the most elegant solutions to this problem.
To install make sure you have nuget installed. Then right-click on your References folder and choose "Add Library Package Reference". Search online for FluentValidation and install the FluentValidation.MVC3 package.
If you look at part two in this series you'll notice that our Widget class is pretty busy. I don't know about you but I don't really like those validation attributes over each property (of course they are needed for client-side javascript validation). Also, for anything but the most trivial application putting all of your business rules in the Validate method is not going to work. FluentValidation will let us abstract the validator attributes and the business rules out into a separate class. Once they're there we can remove and add business rules all day long and we won't break our EF backing model.
Let's get started. Create a new class in your Validators folder called WidgetValidator. Then move all of your rules into it so it looks like this:
using System.Linq; using WidgetApplication.Models; using FluentValidation; namespace WidgetApplication.Validators { public class WidgetValidator : AbstractValidator<Widget> { private const int MIN_RED_TEETH = 0; private const int MAX_RED_TEETH = 6; private const int MIN_BLUE_TEETH = 8; private const int MAX_BLUE_TEETH = 12; public WidgetValidator() { RuleFor(w => w.Name).NotEmpty(); RuleFor(w => w.Name).Length(1, 10); RuleFor(w => w.NumberOfTeeth).NotEmpty(); RuleFor(w => w.Color).NotEmpty(); RuleFor(w => w) .Must(BeValidRedColor) .WithName("NumberOfTeeth") .WithMessage("Wrong red tooth count."); RuleFor(w => w) .Must(BeValidBlueColor) .WithName("NumberOfTeeth") .WithMessage("Wrong blue tooth count."); RuleFor(w => w) .Must(NameIsAvailable) .WithName("Name") .WithMessage(ValidationMessagesResource.NameIsTaken); } private static bool BeValidRedColor(Widget widget) { if (widget.Color.ToUpper().Equals("RED")) { if (widget.NumberOfTeeth < MIN_RED_TEETH || widget.NumberOfTeeth > MAX_RED_TEETH) return false; } return true; } private static bool BeValidBlueColor(Widget widget) { if (widget.Color.ToUpper().Equals("BLUE")) { if (widget.NumberOfTeeth < MIN_BLUE_TEETH || widget.NumberOfTeeth > MAX_BLUE_TEETH) return false; } return true; } private static bool NameIsAvailable(Widget widget) { var context = new WidgetApplicationContext(); var widgets = context.Widgets.ToList(); return (!widgets.Any(w => w.Name.ToLower().Equals(widget.Name.ToLower()))); } } }
That greatly simplifies our Widget class. Now our Validate method just needs to call the WidgetValidator to get any broken rules:
using System.Collections.Generic; using System.ComponentModel.DataAnnotations; using System.Linq; using WidgetApplication.Validators; using FluentValidationResult = FluentValidation.Results.ValidationResult; namespace WidgetApplication.Models { public class Widget : IValidatableObject { public int Id { get; set; } public string Name { get; set; } public int NumberOfTeeth { get; set; } public string Color { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { var validator = new WidgetValidator(); var results = validator.Validate(this); return results.Errors.Select(e => new ValidationResult(e.ErrorMessage, new[] { e.PropertyName }) ); } } }
That's much cleaner. Our Widget class doesn't know about the business rules, all it knows is that the WidgetValidator has them and will return any broken ones through its IValidatableObject Validate wrapper. You'll notice also that pretty much any kind of business rule you can think of can be accomodated in this pattern. There are RuleFor validators for plain old Required and Length attributes, RuleFor validators for cross-property validation on the class between Color and NumberOfTeeth, and in the NameIsAvailable method there is even a call to the database.

My name is James Still and I'm a seasoned software developer living and working in Oregon USA. I'm an avid cyclist, backpacker, reader, stargazer, and I pick at the guitar from time to time. 
