My Resume

  • My Resume (MS Word) My Resume (PDF)


Affiliations

  • Microsoft Most Valuable Professional
  • INETA Community Champion
  • Leader, NJDOTNET: Central New Jersey .NET User Group

Sunday, April 19, 2009

Cleaner Validation with ASP.NET MVC Model Binders & the Enterprise Library Validation Application Block

I accidentally stumbled across an awesome combination the other day:  using the Enterprise Library Validation Block with ASP.NET MVC.  Though I’ve played around with them a few times in the past, this is the first time I’ve really started to apply the Validation block in a serious application, and it just so happened to have an ASP.NET MVC website as its client.  My jaw dropped more and more as I started to realize the awesomeness that was unfolding before me…  hopefully this blog post will do the same (or as close as possible) to you!

Using the Enterprise Library Validation Block

It all started with an innocent enough Model requiring a wee bit of validation that I didn’t feel like hand-writing, so (as usual) I turned to the EntLib library to do it for me.  Applying the Enterprise Library Validation Block was surprisingly simple. 

It all started with a simple enough class (the names have been changed to protect the innocent):

public class Product
{
public int ID { get; set; }
public string Name { get; set; }
public string Description { get; set; }
public double Price { get; set; }
public int Quantity { get; set; }
}



This is basically just a DTO (data transfer object), but this ain’t the Wild West – there are rules, and they need to be followed!  After a few minutes, I’d come up with something like this:

using Microsoft.Practices.EnterpriseLibrary.Validation;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;

public class Product
{
[RangeValidator(
1, RangeBoundaryType.Inclusive,             /* Lower Bound */
int.MaxValue, RangeBoundaryType.Inclusive   /* Upper Bound */
)]
public int ID { get; set; }

// Let's assume that we've got a field length limitation in
// our database of 500 characters, which we'll check for here
[StringLengthValidator(
1, RangeBoundaryType.Inclusive,             /* Lower Bound */
500, RangeBoundaryType.Inclusive            /* Upper Bound */
)]
public string Name { get; set; }

// No rules for the description - anything goes!
public string Description { get; set; }

// The Price can be whatever we want, as long as it's positive
[RangeValidator(0, RangeBoundaryType.Inclusive, double.MaxValue, RangeBoundaryType.Inclusive)]
public double Price { get; set; }

// Same deal with the Quantity - we can never have a negative quantity
[RangeValidator(0, RangeBoundaryType.Inclusive, int.MaxValue, RangeBoundaryType.Inclusive)]
public int Quantity { get; set; }


public bool IsValid()
{
return Validate().IsValid;
}

public ValidationResults Validate()
{
return Validation.Validate<Product>(this);
}
}



There are a couple of cool things I like about this setup:



  1. Declarative validation rules:  These rules are very explicit expression of business logic - there is no “if-else-then”, mumbo-jumbo.  In other words, there isn’t any code to worry about… and no code means no bugs (well, less bugs at least :).  Moreover, if any of these business rules change, it’s very easy to update these attributes without hunting around for that stray line of “if-else” code somewhere.  Lastly, I’ve heard talk of these mystical “business people” who are also able to read and understand simple code; and, if you run into one of these guys/gals they’ll easily be able to verify that you have the rules set properly as well.
  2. All of the validation logic is in one place:  all consumers of this class need to do is set its properties and ask the object whether or not it is valid.  There are no stray “if(string.IsNullOrEmpty(product.Name)” scattered through your code, just “if(product.IsValid())”.  I feel like this approach has a decent amount of cohesion.  Granted, it could be a bit more cohesive if we had, say, a separate “ProductValidator”, but this seems like overkill.  Regardless, it was bugging me enough that I actually created a super-class to encapsulate this logic further of the chain of inheritance and that made me feel a bit more comfortable:
  3. public class SelfValidatingBase
    {
    public bool IsValid()
    {
    return Validate().IsValid;
    }
    
    public ValidationResults Validate()
    {
    return ValidationFactory.CreateValidator(this.GetType())
    .Validate(this);
    }
    }
    
    public class Product : SelfValidatingBase
    {
    // ...
    }


As with pretty much anything, there is at least one glaring drawback to this approach:  there is no “real-time” checking.  That is, this approach allows consumers to set invalid values on these validated properties at any time – possibly overwriting valid values without any checks prior to the update.  I think that as long as your application (i.e. developers) know about this limitation, it’s not so much of an issue, at least not for the scenarios I’ve used it in, so this drawback doesn’t really bother me.



Now, let’s see how this applies to ASP.NET MVC…



The Awesomeness that is ASP.NET MVC’s Model Binders



When it comes to me and ASP.NET MVC’s Model Binders it was love at first site – and I haven’t stopped using them since.  In case you’re not sure what I’m talking about, here’s an example.  Instead of an Action with individual parameters and populating a new instance ourselves like this:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(string username, string message, string userUrl)
{
var comment = new Comment
{
Message = message,
Username = username,
UserUrl = userUrl,
PostedDate = DateTime.Now
};
commentsRepository.Add(comment);
return RedirectToAction("Index");
}



We let the MVC framework populate a new instance for us, like this:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Comment comment)
{
commentsRepository.Add(comment);
return RedirectToAction("Index");
}



I just think that’s beautiful, and so I’ve come to (over?)use Model Binders on my Controller Actions almost exclusively. 



ASP.NET MVC Model Binders + Enterprise Library Validation Block = BFF



The magic that I refer to at the beginning of this post first exposed itself when I inadvertently used one of my Model objects like the one I showed earlier as an Action parameter (which was really only a matter of time given the fact that I’d taken to using them so much!) using MVC’s Model Binding, and then created some validation logic for it (if you’re not sure what I’m referring to in regards to “creating validation logic”, you’ll want to check out this article on MSDN before continuing).  As I started writing my validation logic in my Action and populating the ModelState with my validation errors like so:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Product product)
{
if (!product.IsValid())
{
if(string.IsNullOrEmpty(product.Name))
this.ModelState.AddModelError("name", "Please enter a product name");
if(product.Price < 0)
this.ModelState.AddModelError("price", "Price must be greater than 0");
if(product.Quantity < 0)
this.ModelState.AddModelError("quantity", "Quantity must be greater than 0");

return View(product);
}

productRepository.Add(product);
return View("Index");
}



Now, even if I moved this code outside of my Action, I’d still be pretty embarrassed of it…  but after looking at it for a while I realized that I don’t have to do this after all – the EntLib ValidationResult (usually) maps perfectly to MVC’s Model Binding…  and ModelState errors!  Check out the same code, taking advantage of the EntLib validation results:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Product product)
{
var validationResult = product.Validate();
if (!validationResult.IsValid)
{
foreach (var result in validationResult)
this.ModelState.AddModelError(result.Key, result.Message);

return View(product);
}

productRepository.Add(product);
return View("Index");
}



I added this and awesomeness ensued.  The magic comes from the fact that the Key field of the EntLib ValidationResult is the name of the property which is causing the validation error.  This leads to what I can do in line 8 above, which is just iterate through all of the validation errors and add their message to the ModelState using their Key property, which corresponds to the form id’s that we’re using to populate the model.  Just so you don’t think I’m lying, here’s what the form would look like:

<%= Html.ValidationSummary(
"Create was unsuccessful. 
Please correct the errors and try again.") %>
<% using (Html.BeginForm()) {%>
<fieldset>
<legend>Add New Product</legend>
<p>
<label for="Name">Name:</label>
<%= Html.TextBox("Name") %>
<%= Html.ValidationMessage("Name", "*") %>
</p>
<p>
<label for="Description">Description:</label>
<%= Html.TextBox("Description") %>
<%= Html.ValidationMessage("Description", "*") %>
</p>
<p>
<label for="Price">Price:</label>
<%= Html.TextBox("Price") %>
<%= Html.ValidationMessage("Price", "*") %>
</p>
<p>
<label for="Quantity">Quantity:</label>
<%= Html.TextBox("Quantity") %>
<%= Html.ValidationMessage("Quantity", "*") %>
</p>
<p>
<input type="submit" value="Create" />
</p>
</fieldset>
<% } %>



I Think We Can Do Just a Bit Better…



So, there you have it – easy validation using ASP.NET MVC Model Binders, MVC’s Validation components, and Enterprise Library’s Validation block.  The preceeding should work like a charm, but me being the perpetual perfectionist and idealist saw one more piece of duplication that I wanted to remove.  Namely, the foreach loop used to map the ValidationResults to the ModelState.  Using an extension method to extend the ValidationResults class, this duplication can easily be removed like so:

using System.Web.Mvc;
using Microsoft.Practices.EnterpriseLibrary.Validation;

public static class EntLibValidationExtensions
{
public static void CopyToModelState(this ValidationResults results, ModelStateDictionary modelState)
{
foreach (var result in results)
modelState.AddModelError(result.Key ?? "_FORM", result.Message);
}
}



Now the previous Action looks just a bit cleaner:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(Product product)
{
var validationResult = product.Validate();
if (!validationResult.IsValid)
{
validationResult.CopyToModelState(this.ModelState);
return View(product);
}

productRepository.Add(product);
return View("Index");
}



And with that, I’m happy…  What do you think??

3 comments:

Anonymous said...

A VABModelBinder would keep some of that boilerplate code out of your actions.

http://codebetter.com/blogs/david.hayden/archive/2009/02/03/an-aha-moment-on-mvc-validation-extensibility-in-defaultmodelbinder-bye-to-idataerrorinfo.aspx

Jess Chadwick said...

Oh wow - a custom ModelBinder supporting the Validation Block is even cooler!

Despite my attempts at Googling this, I hadn't seen David's post. I knew that someone had to have written about this before. :)

Tyrone said...

Great article. Very clean and well presented.