ASP.NET MVC Tip #42 – Use the Validation Application Block

In this tip, I demonstrate how you can use the Microsoft Enterprise Validation Application Block within an MVC application to perform both basic and advanced form validation. The Validation Application Block supports a rich set of validators that you can begin using in an ASP.NET MVC application.

In this tip, I show how you can use the Microsoft Validation Application Block to perform form validation within an ASP.NET MVC application. Using the Validation Application Block was surprisingly easy. You can download and start using the Validation Application Block in minutes.

The Validation Application Block uses a model-driven approach to validation. You specify validation rules for your model entities. For example, if you have a Product class then you create validation rules that require the Product.Name property to contain at least 3 characters and the Product.Price property to have a value less than $10.00.

When a validation rule is broken, you can bubble up a validation error message to an ASP.NET MVC view. That way, the user has a chance to fix the error and submit the form again. For example, the view in Figure 1 illustrates an MVC view that displays validation errors bubbled up from the Validation Application Block.

Figure 1 – Validation errors

clip_image002

Using the Validation Application Block in an ASP.NET MVC Application

In order to use the Validation Application Block, you need to download the Microsoft Enterprise Library 4.0 from the Microsoft website:

http://www.microsoft.com/downloads/details.aspx?FamilyId=90DE37E0-7B42-4044-99BE-F8ECFBBC5B65&displaylang=en&hash=NdKYzXa4U%2beZdzZa%2buGPu%2flKE%2fPfL0GjN6Z1dqDOZX6rPWcMMhAC0c9v5ayzqms35yOrCB%2fAbXBCXbL1z%2f4bhA%3d%3d

After you install the Enterprise Library, the Enterprise Library assemblies can be found in the following folder on your computer:

C:\Program Files\Microsoft Enterprise Library 4.0 - May 2008\Bin\

In order to use the Validation Application Block in an MVC application, you need to add a reference to the Microsoft.Practices.EnterpriseLibrary.Validation.dll assembly. Within Visual Studio, select the menu option Project, Add Reference, select the Browse tab, and browse to the folder that contains the Enterprise Library assemblies (see Figure 2). Click the OK button to add a reference to the assembly.

Figure 2 – Adding a reference to the Validation Application Block

clip_image004

Specifying Validation Rules

The Validation Application Block supports two methods of specifying validation rules. You can use an attribute approach or you can use a configuration approach.

When you use an attribute approach, you decorate properties of a class with validator attributes. The Validation Application Block supports a rich set of validators (too many to list here). Here is a list of some of the more useful ones:

· NotNullValidator – Validates that a property does not have the value Null.

· RangeValidator – Validates that the value of a property falls between a specified minimum and maximum range.

· RegexValidator – Validates that a property value matches a regular expression.

· StringLengthValidator – Validates that the value of a string property has a specified minimum and maximum number of characters.

Of course, you also can create custom validators. The Validation Application Quickstart project included with the Enterprise Library download includes sample custom validators such as a USStateValidator.

The class in Listing 1 illustrates how you can use these validators to specify validation rules on a class.

Listing 1 – Models\Movie.cs

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using Microsoft.Practices.EnterpriseLibrary.Validation.Validators;
using MvcValidation;

namespace Tip33.Models
{
    public class Movie : IValidated
    {
        public int Id { get; set; }
        
        [StringLengthValidator(3, 50, MessageTemplate="You must supply a title between 3 and 50 characters.")]
        public string Title { get; set; }

        [StringLengthValidator(3, 50, MessageTemplate="You must supply a director between 2 and 50 characters.")]
        public string Director { get; set; }

        public decimal BoxOfficeTotals { get; set; }
    }
}

In Listing 1, the StringLengthValidator is applied to both the Title and Director property. This validator is used to ensure that these properties get a value assigned to them.

The Validation Application Block supports an alternative method of specifying validation rules. Instead of using attributes, you can specify the validation rules in an XML configuration file. I won’t explore this option in this tip, but this is an attractive option when you want to keep your entity classes pure.

Performing Validation with the Validation Application Block

After you specify your validation rules, you can check whether or not a particular class violates the rules by calling the Validation.Validate() method. For example, you can validate an instance of the Movie class named newMovie by calling the Validate() method like this:

var results = Validation.Validate(newMovie);

The Validate() method returns a ValidationResult object. The ValidationResult object supports a property named IsValid that indicates whether or not the class being validated failed validation.

The ValidationResult class is a collection. You can determine exactly which validation rules a class violated by iterating through the collection of broken validation rules represented by the ValidationResult class.

Using the Validation Application Block in an ASP.NET MVC Application

Let’s walk through a complete sample MVC application that uses the Validation Application Block. We’ll use the Validation Application Block with a simple movie database application to validate new movies before the movies are submitted to the database.

The controller for the Movie database application is contained in Listing 2.

Listing 2 – Controllers\HomeController.cs

using System.Web.Mvc;
using Microsoft.Web.Mvc;
using MvcValidation;
using Tip33.Models;
using Tip42.Models;

namespace Tip42.Controllers
{
    [HandleError]
    public class HomeController : Controller
    {

        private MovieRepository _repository;

        public HomeController()
        {
            _repository = new MovieRepository();
        }

        public ActionResult Index()
        {
            var movies = _repository.SelectAll();
            return View("Index", movies);
        }

        [AcceptVerbs("GET")]
        public ActionResult Create()
        {
            return View("Create");
        }

        [AcceptVerbs("POST")]
        public ActionResult Create(FormCollection form)
        {
            var movieToCreate = new Movie();

            try
            {
                UpdateModel(movieToCreate, new[] { "Title", "Director", "BoxOfficeTotals" });
                _repository.Add(movieToCreate);
            }
            catch (ValidationException vex)
            {
                ViewData.ModelState.CopyValidationExceptions(vex);
                return View("Create", movieToCreate);
            }
            catch
            {
                return View("Create", movieToCreate);
            }

            return RedirectToAction("Index");
        }
    }
}

The Home controller exposes three actions: Index(), Create(), and Create(FormCollection form). The Index() action displays a list of existing movies from the database. The first Create() method displays a form for inserting a new movie. The second Create() method actually performs the validation and adds the movie to the database.

Notice that the Home controller class uses a MovieRepository to perform all of its database access operations. The MovieRepository class is contained in Listing 3.

Listing 3 – Models\MovieRepository.cs

using LinqToSqlExtensions;
using System.Linq;
using MvcFakes;
using MvcValidation;
using Tip33.Models;
using System.Collections.Generic;

namespace Tip42.Models
{
    public class MovieRepository
    {
        private IDataContext _dataContext;

        public MovieRepository()
        {
            _dataContext = new DataContextWrapper("conMovieDB", "~/Models/MovieDB.xml");
        }

        public List<Movie> SelectAll()
        {
            return _dataContext.Select<Movie>().ToList();
        }


        public Movie Add(Movie movieToAdd)
        {
            movieToAdd.Validate<Movie>();
            _dataContext.Insert(movieToAdd);
            return movieToAdd;
        }

    }
}

The MovieRepository.Add() method adds a new Movie to the database. Notice that before the Movie is inserted into the database, the Validate() method is called on the Movie object. If the Movie object violates any of the validation rules then calling the Validate() method throws a ValidationException.

Where does the Validate() method on the Movie class come from? I created a Validate() extension method. This extension method is added to any class that implements the IValidated interface (like the Movie class). The code for the Validate() extension method is contained in Listing 4.

Listing 4 – MvcValidation\ValidationExtensions.cs

using Microsoft.Practices.EnterpriseLibrary.Validation;

namespace MvcValidation
{
    public static class ValidationExtensions
    {
        public static void Validate<TEntity>(this IValidated entity)
        {
            var results = Validation.Validate<TEntity>((TEntity)entity);
            if (!results.IsValid)
                throw new ValidationException(results);
        }


    }
}

The extension method in Listing 4 simply calls the Validation Application Block Validation.Validate() method to retrieve a ValidationResult. If there are validation violations, a ValidationException that represents the validation violations is thrown.

I created one more extension method that I use in the Home controller: the CopyValidationExceptions() extension method. This extension method copies validation errors from a ValidationException to a ModelState object. The code for the CopyValidationExceptions() method is contained in Listing 4.

Listing 4 – MvcValidation\ModelStateDictionaryExtensions.cs

using System.Web.Mvc;

namespace MvcValidation
{
    public static class ModelStateDictionaryExtensions
    {
        public static void CopyValidationExceptions(this ModelStateDictionary modelState, ValidationException validationException)
        {
            foreach (var vex in validationException.ValidationResults)
            {
                modelState.AddModelError(vex.Key, null, vex.Message);
            }
        }

    }
}

The CopyValidationExceptions() extension method is used within the Create() action in the Home controller like this:

[AcceptVerbs("POST")]
public ActionResult Create(FormCollection form)
{
    var movieToCreate = new Movie();

    try
    {
        UpdateModel(movieToCreate, new[] { "Title", "Director", "BoxOfficeTotals" });
        _repository.Add(movieToCreate);
    }
    catch (ValidationException vex)
    {
        ViewData.ModelState.CopyValidationExceptions(vex);
        return View("Create", movieToCreate);
    }
    catch
    {
        return View("Create", movieToCreate);
    }

    return RedirectToAction("Index");
}

If there is a ValdationException, the validation violations are copied to ModelState so that the validation errors can be rendered in the MVC view.

I didn’t have to do anything special to display the validation errors in an MVC view. The Create view is contained in Listing 5.

Listing 5 --- Views\Home\Create.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Create.aspx.cs" Inherits="Tip42.Views.Home.Create" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>Create Movie</title>
    <style type="text/css">
    
    .input-validation-error
    {
        border: solid 2px red;
        background-color:yellow;
    }
    
    </style>
</head>
<body>
    <div>
    
    <h1>Add Movie</h1>
    
    <form method="post" action="/Home/Create">
    
    <%= Html.ValidationSummary() %>
    
    
    <label for="Title">Movie Title:</label>
    <br />
    <%= Html.TextBox("Title") %>
    
    <br /><br />
    <label for="Director">Movie Director:</label>
    <br />
    <%= Html.TextBox("Director") %>
    
    <br /><br />
    <label for="BoxOfficeTotals">Box Office Totals:</label>
    <br />
    <%= Html.TextBox("BoxOfficeTotals") %>
    
    <br /><br />
    <input type="submit" value="Add Movie" />
    
    </form>
    
    
    </div>
</body>
</html>

Notice that the Create view takes advantage of the Html.ValidationSummary() HTML helper method to display the errors. Furthermore, when there is a validation error associated with a particular TextBox, the Html.TextBox helper renders an input-validation-error CSS class automatically. The style sheet defined at the top of the view causes input fields associated with a validation error to appear with a thick red border and a yellow background (see Figure 3).

Figure 3 – Validation errors

clip_image002[1]

Summary

To be honest, I assumed that getting the Validation Application Block to work with an ASP.NET MVC application would take a lot of effort. Instead, I was able to fully integrate the Validation Block with my Movie database application within a couple of hours. The Validation Application Block is an excellent option for providing validation for an ASP.NET MVC application.

Download the Code

17 Comments

  • Cool!
    Thanks Stephen ~

  • This looks nice too. It seems that whatever you use, you should make sure you can get the errors into the modelstate. One downside with the validation block is that you can't use it on generated code, it has no concept of a buddy class that the validation in dynamic data has.

    In fact, the validation block seems to overlap with System.ComponentModel.DataAnnotations, what's Microsofts idea here?

  • Its great to see you diving into all the new functionality as well as pulling in other libraries to help get a usable solution in place.

    That being said, I would seperate the validation from the library and stay away from throwing an exception on validation errors. I know this is similar to the linq to sql model, but I would argue that exceptions are for exceptional cases rather than a normal course for application code(like validation errors).

    I would probably wrap the repository and the validation in a composite service that takes care of the combined concerns. ... that my 2 cents...

  • @Eric -- thanks for the feedback, always appreciated!

  • Great post... I love model-based validation.

    I did something almost identical but using the Castle Validation Block instead + jQuery to do client validation.

    I completely forgot about Microsoft's Validation Application Block and like you I would have assumed it would be hard to use with MVC.

    It should be pretty easy to generate the client script to perform client validation with jQuery and should look very similar to my implementation.

    The link if anyone is interested is http://www.emadibrahim.com/2008/09/08/client-server-side-validation-in-aspnet-mvc/

  • @eibrahim -- thanks for the link! Great article, I really like how you integrated JQuery. Cool stuff.

  • Great post but one questions Why is the validation method is called in Repository but not in the controller within the try block? IMHO data layer should not know care about validation of business rules. Data quality validation in DAL is OK, no?

  • Thank u for giving article that easy to understand.

  • I'm testing an approach that moves this validation out of the controller methods, and into a (Preview5) custom model binder instead.

    If I can get it working, I think it is a cleaner approach, since it blends well with the existing Preview5 validation (that is lacking, hence my desire to use VAB) and it allows generic VAB validation/error reporting.

  • Hey,

    I just downloaded your sample and the Enterprise Library 4.0

    I unzipped your file and open the .sln file.

    Now i get this error:"*.csproj the project type is not supported by this installation"

    I work daily as C# developer in VS 2008 and i never had this problem.

    Did i something wrong, or did i forget something ?

    Please let me know.

    Thanks

  • Hey,

    I just downloaded your sample and the Enterprise Library 4.0

    I unzipped your file and open the .sln file.

    Now i get this error:"*.csproj the project type is not supported by this installation"

    I work daily as C# developer in VS 2008 and i never had this problem.

    Did i something wrong, or did i forget something ?

    Please let me know.

    Thanks

  • I downloaded the Microsoft Enterprise Library 4.0. Added reference to the Microsoft.Practices.EnterpriseLibrary.Validation.dll but could not find the interface IValidated. Could you please help?

  • He is my small example of using MVC with ModelBinder and VAB:

    http://navin.biz/MvcValidation/

  • @Agnes -- The IValidated interface is not part of the Enterprise library -- its included in the MvcValidation project in the download at the end of the blog entry above (Click the _Download the Code_ link).

  • Asp net mvc tip 42 use the validation application block.. Not so bad :)

  • Asp net mvc tip 42 use the validation application block.. Nifty :)

  • Asp net mvc tip 42 use the validation application block.. Very nice :)

Comments have been disabled for this content.