Enterprise Library Validation Application Block with ASP.NET MVC

Several weeks ago (before the release of the CTP5 of MVC) I posted a way to leverage the EntLib Validation Application Block with MVC. Since then CTP5 has been released and this finally includes a mechanism for reporting validations.  It is important to note that it does not include the mechanism for how to do validation but rather how to report validations.  This is actually a great thing because, first off, it allows the flexibility to utilize whatever validation framework you please rather than forcing you into a specific framework.  Additionally, it encourages validations to be handled in the business/domain layer rather than the presentation layer.  Stephen Walther shows an example here for how to leverage the EntLib VAB with the latest version of MVC. In this post, I'm going to expand on that including discussing specific issues that need to be addressed in more real-world applications.

Let's take an example:

In the simple case, let's say we pass a Contact object as the Model on to the View. To display and validation the first name we can simple do this:

<%=Html.TextBox("FirstName")%>
<%=Html.ValidationMessage("FirstName") %>

Then in our server side C# code during postback we can do this:

foreach (var validationResult in validationResults)
{
    this.ViewData.ModelState.AddModelError(validationResult.Key, null, validationResult.Message);
}

This is all well and good because the validationResult.Key is going to be "FirstName" (e.g., if the first name was not valid).  Basically it is just the property name.

This worked fine because the property name matched the string we gave the the Html.TextBox() and Html.ValidationMessage() methods. We don't have to look far to see that this will not always be this simple.  Let see what it looks like to show our Contact's Street Address:

<%=Html.TextBox("Address.StreetAddress")%>
<%=Html.ValidationMessage("Address.StreetAddress")%>

Notice that now we have to specify "Address.StreetAddress" because Address is a property on the Contact object. If the street address is not valid, the C# code above will not work because the validationResult.Key is going to be "StreetAddress" but the key we need for the MVC ViewPage is "Address.StreetAddress". So basically we need is a helper method that is going to translate the Key from the ValidationResult into the key needed for the ViewPage. That is, we want to translate "StreetAddress" into "Address.StreetAddress" in this case. Even more specifically, we have to set the "prefix" correctly because we know the property names are going to match.

There are several ways to solve this. One way is to use a simple property prefix map like this:

static Dictionary<Type, string> propertyPrefixMap = new Dictionary<Type, string>()
{
    { typeof(Address), "Address" },
    // Add other types here
};
 
public static string ToMvcKey(this ValidationResult validationResult)
{
    string prefix;
    propertyPrefixMap.TryGetValue(validationResult.Target.GetType(), out prefix);
 
    if (string.IsNullOrEmpty(prefix))
    {
        return validationResult.Key;
    }
    else
    {
        return prefix + "." + validationResult.Key;
    }
}

A couple things of note.  I'm using a C# 3.0 collection initializer on the dictionary here.  Then notice I'm using the "this" keyword to make ToMvcKey() an extension method.  Now I can change the original C# foreach loop to look like this:

foreach (var validationResult in validationResults)
{
    this.ViewData.ModelState.AddModelError(validationResult.ToMvcKey(), null, validationResult.Message);
}

There are many ways to make this solution more flexible. For example, we could create an overload for our extension method that allows us to pass a dictionary in since different objects might be in different places of the object hierarchy depending on the ViewPage. In any case, the flexibility that the MVC framework gives you to perform various tasks many different ways is a great thing.