Posts
208
Comments
1144
Trackbacks
51
Leverage EntLib Validation Application Block in MVC

UPDATE: This post is now out of date as of the CTP5 release of the MVC framework. For the lastest version, see this post here.


One of the areas of MVC that is still an open area in development is how best to handle UI validations.  There are numerous approaches currently being examined.

The Enterprise Library's VAB is great because it allows you to specify all your validation logic in your business layer where it belongs rather than in your UI.  Additionally, it comes with the PropertyProxyValidator to automatically display validation messages in the UI and avoid having to put hundreds of validators on a page.  However, the PropertyProxyValidator is a traditional ASP.NET validator control and does not work in the MVC framework.

The good news here is that duplicating the functionality of the PropertyProxyValidator in the MVC framework is not difficult.  Currently, the MVC framework comes with some built-in extension methods to enable the developer to easy create UI elements hanging off the HtmlHelper property.  I've written a couple of similar extension methods following the same pattern that essentially produce the same output as the ProperyProxyValidator.  This enables us to write UI code without cluttering our mark up with a ton of validator controls:

   1:  <%=Html.TextBox("FirstName", ViewData.Model.Contact.FirstName)%>
   2:  <%=Html.Validator("FirstName", ViewData.Model.ValidationResults)%>

Notice that a property called "ValidationResults" is being passed in to this Validator.  When the form is submitted, it goes to a controller action (let's say "Save" for this example):

   1:  public ActionResult Save(int id)
   2:  {
   3:      Contact contact = new Contact();
   4:      contact.ContactID = id;
   5:      BindingHelperExtensions.UpdateFrom(contact, Request.Form);
   6:   
   7:      ValidationResults validationResults = Validation.Validate(contact);
   8:      if (validationResults.IsValid)
   9:      {
  10:          contactManager.SaveContact(contact);
  11:          return RedirectToAction(Views.Index);
  12:      }
  13:      else
  14:      {
  15:          ContactViewData contactViewData = new ContactViewData(contact);
  16:          contactViewData.ValidationResults = validationResults;
  17:          ViewData.Model = contactViewData;
  18:          return View(Views.EditContact);
  19:      }
  20:  

So if the contact object in this example is valid, we save it to the database and proceed as normal to the next redirect action.  However, if the contact is not valid, we redisplay the edit contact page again and this time we pass the ValidationResults along with the existing contact object in the Model to the View.  This Validator control will grab the appropriate validation messages for the specified control/property and display any validation messages for that specific control.  The following is the complete code for the Validator extension methods:

   1:  public static class ValidationControlExtensions
   2:  {
   3:   
   4:      public static string Validator(this HtmlHelper html, string controlToValidate, ValidationResults validationResults)
   5:      {
   6:          return Validator(html, controlToValidate, validationResults, new { style="color:Red;" });
   7:      }
   8:   
   9:      public static string Validator(this HtmlHelper html, string controlToValidate, ValidationResults validationResults, object htmlAttributes)
  10:      {
  11:          return Validator(html, controlToValidate, controlToValidate, validationResults, htmlAttributes);
  12:      }
  13:   
  14:      public static string Validator(this HtmlHelper html, string controlToValidate, string propertyName, ValidationResults validationResults, object htmlAttributes)
  15:      {
  16:          if (validationResults == null)
  17:          {
  18:              return null;
  19:          }
  20:          
  21:          var filteredResults = validationResults.Where(r => r.Key == propertyName);
  22:          if (filteredResults.Count() == 0)
  23:          {
  24:              return null;
  25:          }
  26:   
  27:          StringBuilder errorMessage = new StringBuilder();
  28:          foreach (ValidationResult validationResult in filteredResults)
  29:          {
  30:              errorMessage.Append(validationResult.Message);
  31:              errorMessage.Append("<br/>");
  32:          }
  33:   
  34:          TagBuilder builder = new TagBuilder("span");
  35:          IDictionary<string, object> attribueDictionary = new RouteValueDictionary(htmlAttributes);
  36:          builder.Attributes = ToStringDictionary(attribueDictionary);
  37:          builder.InnerHtml = errorMessage.ToString();
  38:          return builder.ToString();
  39:      }
  40:   
  41:      /// <summary>
  42:      /// ToStringDictionary is Internal to System.Web.Mvc.HtmlHelper
  43:      /// </summary>
  44:      /// <typeparam name="TKey"></typeparam>
  45:      /// <typeparam name="TValue"></typeparam>
  46:      /// <param name="dictionary"></param>
  47:      /// <returns></returns>
  48:      internal static Dictionary<string, string> ToStringDictionary<TKey, TValue>(IDictionary<TKey, TValue> dictionary)
  49:      {
  50:          if (dictionary == null)
  51:          {
  52:              return new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
  53:          }
  54:          return dictionary.ToDictionary<KeyValuePair<TKey, TValue>, string, string>(entry => Convert.ToString(entry.Key, CultureInfo.InvariantCulture), entry => Convert.ToString(entry.Value, CultureInfo.InvariantCulture), StringComparer.OrdinalIgnoreCase);
  55:      }
  56:  }

One note here is that you can see I'm using a class called TagBuilder inside my method.  This is actually an internal class in the System.Web.Mvc assembly.  I just cracked open Reflector and did a complete copy/paste of that code into my own assembly so that I could leverage it.  But what would be really nice is if Microsoft would change the accessibility on that class to public to more easily enable scenarios like this when people want to build their own control extension methods.

posted on Tuesday, June 17, 2008 3:47 PM Print
Comments
Gravatar
# re: Leverage EntLib Validation Application Block in MVC
Erik Lane
6/19/2008 10:29 AM
Nice post and thanks for the tip and example using the Entlib Validation block with the MVC framework.
Gravatar
# re: Leverage EntLib Validation Application Block in MVC
tasarım
10/5/2008 6:25 AM
Very nice post thank you. Been reading posts about MVC for a month. There are not many posts on details of validation or using it differently. I guess people wait for the MVC beta so they don't have to change their code for every preview.
Gravatar
# re: Leverage EntLib Validation Application Block in MVC
Steve
10/5/2008 7:26 AM
I'd recommend checking out this post which has been update for Validation with the latest release of MVC: http://geekswithblogs.net/michelotti/archive/2008/10/04/enterprise-library-validation-application-block-with-asp.net-mvc.aspx

Post Comment

Title *
Name *
Email
Comment *  
Verification

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud