There have been many validation frameworks over the years, but I find that they don’t work the way I would like, or the syntax is confusing, or they can not accommodate complex business validation I need. I have basically used a custom framework that involves a way to call validation methods and collect the results in the form of List<string>. These validation methods gets called in the Business Layer and passed to the consuming layer (normally a UI or WCF layer) via a MessageManager class.
Recently I started a new project and decided to look around at the latest and greatest to see what was available. I came across the Fluent Validation (FV) framework on CodePlex. It had all the structures that my custom framework had, but uses the fluent interface with lambda expressions which I really liked. I was able to integrate the Fluent Validation framework in with my custom MessageManager by just overloading the Add method to take type ValidationResult (which is the returned set of results when validation is executed). Below is a description of how to set up your business layer to use the FV framework. In this sample project I’m using a modified Entity Framework set up from my last post.
The first thing to do is install Fluent Validation (via NuGet) to your Model and Business Projects.
The Validation rules will get written in the Model project per Entity and get called in the Business methods where needed.
To keep things organized you will need to create two folders (Custom and Validation) in the Model Project under the root. The Custom folder is used to extend the Entity classes created from the Entity Framework. Since they are generated as partial classes this is very easy. The Validation folder is used for the Fluent Validation classes.
I’m not going to spend too much time on the Custom classes because this blog is not about extended the partial classes created by Entity Framework. In the sample application that I always use, Meaney Golf Pool, users can join a Golf Pool Event (for fun of course) that runs for the 4 majors (Masters, US Open, British Open, and The Players Championship) of the Golf Season. The rules are configurable, but it normally goes something like this:
- Pick 7 Golfers from the Field
- You can only pick 3 from top ten according to www.officialworldgolfranking.com
- Pick Tiebreaker incase multiple people pick the same 7. Tiebreaker is winning golfers score, something like -7.
- After the tournament is over the total winnings for each golfer is entered and the group of 7 that earns the most money wins.
A good use of extending the EF generated class in this project is on the UserEntry entity. This entity represents a users group of 7 golfers. It has a sequence primary key (PK), a foreign key (FK) back to the user the entry belongs, and FK to the event the user joined, Name, and Tiebreaker.
Notice there is not a property to keep the TotalWinnings at the entry level. In this application that value is calculated from adding the winnings from EntryGolfers collection. Since this value gets displayed in the GUI it makes sense that it be a property on the UserEntry class. To accomplish this I add a new class to the Custom directory called UserEntry (delete the Custom from the namespace and make the class public and partial).
Now you can add properties to extend the EF generated UserEntry class. I’m using a lazy loading technique so the property only gets loaded once per object lifecycle.
With all this out of the way we can now dive into using the Fluent Validation Framework. In this example I’m going to validate the following Business Rules on the UserEntry class.
- Every UserEntry must have a Name
- EntryGolfer (cross reference that links UserEntry to Golfer table) must have UserEntryId and GolferId
- UserEntry.TopTenCount (count of golfers in top ten rankings) does not exceed the configured GolfEvent value.
- A golfer can only be selected once per entry (no duplicates).
Create a class in the Validation directory named UserEntryValidation and make the class inherit from AbstractValidator<UserEntry>. AbstractValidator<T> is the bas
s class that all FV class must inherit from. Using lamba expressions you can implement simple and complex validation. The framework comes with numerous out of the box extension methods (NotNull, LessThanOrEqualTo, etc…)that make it easy implement your validation.
To start creating validation rules you need to call the RuleFor method in the constructor. RuleFor method takes lambda expression for the property you’re validating. The first RuleFor method checks the Name property is not null. The second method validates that each EntryGolfer in the EntryGolfers collection has a GolferId and UserEntryId. This is done by using the SetCollectionValidator method with the class that validates one EntryGolfer.
The third method validates that the UserEntry does not have more Top 10 golfers then the event allows. Similarly the fourth method validates the UserEntry does have more golfers then allowed. The final method validates all golfers per entry are unique. This one is more complex and works as follows. The GolferIdList is just a list of int’s (List<int>) with the primary key of the golfers for a UserEntry. The first part of the lambda expressions, ue.GolferIdList.GroupBy(i=> i), groups the int values. If a golfer is in the list twice that grouping will have a count greater than 1. The second part, .Where(grp => grp.Count() > 1).Count(), filters the result set where grouping count is greater than 1 and then counts the number of groups. The next part, .Equal(0), says that the result from the grouping count is not equal to 0 show the validation message.
The last part of this blog shows how to call this validation in the business tier. My business layer is a simple static class with methods to execute business requirements. We are going to specifically look at the AddGolferToEntry method.
This method takes a GolferId, UserEntryId, and MessageManager as paramters. The MessageManager class is used to pass validation results between the UI and Business layers. In this simplified Business layer I am accessing the EF context directly instead of using a loose coupling interface pattern to data access layer. First the UserEntry is retrieved form the database. Now a new instance of EntryGolfer is set using the GolferId and UserEntryId and is added to the UserEntry.EntryGolfers collection. The next two lines are where the FV classes are executed.
UserEntryValidation validation = new UserEntryValidation();
ValidationResult results = validation.Validate(userEntry);
The first line creates a new instance of the validation class created earlier. The second calls the Validate method with instance of UserEntry that needs to be validated and return the results into a ValidationResult instance. The MessageManager is overloaded to take ValidationResults in Add method.
In review I shown how to integrate the Fluent Validation Framework into the Model classes created by Entity Framework. I have demonstrated simple and more complex validation using FV with lambda expressions. In my next blog entry I will go into details of the Meaney Golf Pool sample applications and provide everything you need to get this running locally.