LinkedIn

Monday, 26 September 2011

Entity validation, unit testing and MVC 3 with Fluent - RuleSets and Predicates


Morning all,

I have been looking at validation for my current project. We are building an MVC 3 site that calls a wcf service later that is wrapped around Crm 2011.

Initially I was looking at data annotations out of the box. These work really well and MVC 3 works seamlessly with these with client validation and unobtrusive JQuery. 

 When  I came to look at rulesets however it seems a lot more complicated. I them stumbled upon Fluent validation:

This is a very user friendly clean way of writing validation on your entities in the same way that data annotations are written. The difference came when I started to look at rule sets which you will see in my attached screen shots.

Firstly I defined 2 classes in my domain model. Thsese are called ContactForValidationPOC and AddressForValidationPOC as shown below:

 ----------------------------------------------------------------------------------------------------------



As you can see there are no data annotations and ContactForValidationPOC has a complex property of Addresses.

We are going to write some rulesets that will validate the contact for a STAFF ruleset and a public ruleset. (Note that there are not real world validation needs as of yet,it's just proof of concept code.)

You will notice that at the top of the ContactForValidationPOC class we have an attribute shown as follows:
[Validator(typeof(ContactValidator))] 
This tells the class that there is a validator class call ContactValidator somewhere.
Also please note that I have referenced the correct Fluent  validation dlls in my project. 
You can find the details of this here:
http://fluentvalidation.codeplex.com/ 
So back to the ContactValidator. Here it is:
You can see that we have 2 rule sets. The first is a staff rule set and the second is a public rule set. 
They are quite similar and the only difference really is that the Age must be over 18 for a staff member. 
Both rule sets also use a .Must predicate validator. 
This is a great place to put more complex logic.

Here we need to ensure that there is at least 1 address in the Addresses collection that is marked as IsPrefferedAddress.
Ok, so that is all done, now we need to unit test or validation rules.  
 
So I have a unit test written here. Firstly I set up a new ContactForValidationPOC entity and add a couple of addresses to it. 
I then call Validate on it and ask it to validate using the Staff rule set. After validation we check that the ValidationResult (result) is valid 
and then do a simple check for the number of validation errors. Obviously my test could be more detailed in a real world example. 
As I debug through my test I can see that my entity is not valid (IsValid = false
and it has a collection of validation errors on it.


This is good as we didn't enter a first name, our Age is under 18 and we set all AddressForValidationPOC's in the Addresses collection to IsPrefferedAddress = false.

Ok so we now know that our validation is working correctly on the server side.
We haven't even touched the MVC code yet.

I have whipped up a quick MVC controller with a view and have disabled the settings 
in the web.config that make client side validation work as shown in the following picture:


Note: they should be set to false to prove that server side validation is still working correctly now that we are validating through MVC.

Here are my controller and view: 
Note: I have just forced 2 addresses into the ContactForValidationPOC entity to make things easier, these are both set to IsPrefferedAddress = false 

Here is the view that kicked off the validation:


You can see that, when debugging the controller we get the same validation results. 
IsValid = false and a number of errors relating to our Staff rule set.

Note that in our controller we could have control over what rule set is loaded if we so desired:


We could use some logic to check what type of rule set to load, staff or public.

Now I will change the web.config setting to ensure that client side validation gets included:

We now can fire up the view in a web browser and see client side validation in action:

Well that is it, a high level explanation of domain entity server and client validation using Fluent, unit tests and MVC.

Some positive points:
Some possible negatives:
  • Only the following validators are supported on the client:
    - NotNull/NotEmpty

    - Matches (regex)
    -
    InclusiveBetween (range)
    - CreditCard

    - Email

    - EqualTo (cross-property equality comparison)

    - Length
  • Cant think of any more at the moment.
I hope this was helpful.


RuSs