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

image

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 FilesMicrosoft Enterprise Library 4.0 – May 2008Bin

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

image

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 – ModelsMovie.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 – ControllersHomeController.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 – ModelsMovieRepository.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 – MvcValidationValidationExtensions.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 – MvcValidationModelStateDictionaryExtensions.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 — ViewsHomeCreate.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

image

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

Discussion

  1. cizzo says:

    Cool!
    Thanks Stephen ~

  2. http:// says:

    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?

  3. Eric Hexter says:

    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…

  4. http:// says:

    @Eric — thanks for the feedback, always appreciated!

  5. eibrahim says:

    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/…/client-server-side-vali...

  6. http:// says:

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

  7. http:// says:

    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?

  8. Goolt says:

    Thank u for giving article that easy to understand.

  9. http:// says:

    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.

  10. http:// says:

    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

  11. http:// says:

    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

  12. http:// says:

    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?

  13. koistya says:

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

    http://navin.biz/MvcValidation/

  14. http:// says:

    @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).

  15. tasarım says:

    I really enjoy reading your blog. I do learn alot. I am pretty new to ASP.NET MVC. But you do write very well. I learn alot from your blog. Thanks mate.

  16. http:// says:

    Can this method be utilized if validation is dependent on another field? For instance, lets say I have two fields on a form:

    txtUserName
    txtFirstName

    Now, if the user hits the page they must input a value in one of the fields but both is not required. So, I’d only display a message if both fields are left empty when they try to post.

    Can this be handled by this method of validation or am I better off utilizing the method of validation found here?

    weblogs.asp.net/…/…form-posting-scenarios.aspx

    Thanks.

  17. Daya shankar says:

    Hi Stephen,
    I really enjoy reading your blog. I do learn alot. I want to do same type of validation but my message will come from resource file. For that I am doing following thing but for some reason it is not working

    [NotNullOrEmptyValidator(MessageTemplateResourceName = “must”, MessageTemplateResourceType = typeof(Resources.PortalMain))]

    Where “must” is my name and PortalMain is my resource file name.
    I m not getting any error but message is also not coming.

    Any help would be appreciated…

  18. markoh says:

    Hey Stephen,
    I enjoy reading your blog and your contributions at ASP.NET website. However, none of your samples deal with the case when we use validation with Views where we have a create method that loads up some untyped ViewData. For instance, my create methods load up all kinds of data for ViewData, typically select lists and then in the view the DropDowns are bounded to them. This works perfectly. But when there is an validation error/exception in Create (POST) method, then these untyped data is unavailable so there is an exception like this: “There is no ViewData item with the key ‘Cities’ …”

    The only way to get rid of this is to populate ViewData in my catch statement. However, I am not sure whether this is the right way – actually I believe it is not or at least to say a very ugly way to do it.

    Hope you reply to my question since it is very common also on ASP.NET forums but has not yet been replied.

    Best regards

  19. MOV to DVD says:

    HI,
    I want to implement a create functionality from a popup how can i invoke a create method from the popup(which is a .ascx user control)Please help.
    =================================================
    Thanks in advance

  20. Ya really good article. I understand it very well.

  21. Great Post! This code is going to be very helpful.

  22. something completely different?

  23. Ya really good article. I understand it very well.

  24. in the view the DropDowns are bounded to them. This works perfectly. But when there is an validation error/exception in Create (POST) method, then these untyped data is unavailable so there is an exception like this: “There is no ViewData item with the key ‘Cities’

  25. there is an validation error/exception in Create (POST) method, then these untyped data is unavailable so there is an exception like this: “There is no ViewData item with the key ‘Cities’

  26. then these untyped data is unavailable so there is an exception like this: “There is no ViewData item with the key ‘Cities’

  27. something completely different?

  28. This code is going to be very helpful for me.

  29. Wich kind of validation do you recomend?
    VAB or DataAnnotations?

  30. f34h4 It looks like DataContextExtensions.cs line 45 of the Save method should pass the primaryKeyName through to Update.