ASP.NET MVC Tip #43 – Use Data Annotation Validators

In this tip, I demonstrate how to take advantage of the validators from the System.ComponentModel.DataAnnotations namespace in an MVC application. You can take advantage of these validators to validate form data before submitting the form data into a database.

In my previous tip, I explained how you can take advantage of the validators included with the Microsoft Enterprise Library Validation Application Block to validate an entity before submitting the entity to a database. The Validation Application Block supplies you with a very rich set of validators that support advanced validation scenarios. You can read about using the Validation Application Block in an MVC application here:

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

In this tip, I explore an alternative set of validators. In this tip, I discuss the validators included in the System.ComponentModel.DataAnnotations namespace. These are the very same validators as you use in a Dynamic Data application. You get these validators when you install Visual Studio Service Pack 1.

I found the Data Annotation validators to be easier to use in simple validation scenarios than the validators included with the Validaiton Application Block. If you want to perform simple validation tasks such as verifying required fields, verifying email addresses, and checking the length of form fields values then the Data Annotation validators are a great option.

Overview of the Data Annotation Validators

The Data Annotation validators are contained in the System.ComponentModel.DataAnnotations namespace. In order to use these validators in an MVC application, you must have Visual Studio Service Pack 1 installed. Furthermore, you must add a reference to the System.ComponentModel.DataAnnotations assembly (located in the Global Assembly Cache).

There are 5 validation attributes in the DataAnnotations namespace:

· DataType – Enables you to verify that a property matches a certain type. Valid types are Custom, DateTime, Date, Time, Duration, PhoneNumber, Currency, Text, Html, MultiLineText, EmailAddress, Password, and URL. You also can specify a custom type.

· Range – Enables you to validate whether a property value falls between a specified minimum and maximum value.

· RegularExpression – Enables you to validate whether a property value matches a regular expression pattern.

· Required – Enables you to validate whether a property has a value. A property value that consists only of whitespace fails validation.

· StringLength – Enables you to validate whether a string is less than a specified maximum length.

This is a very basic set of validators. They cover the most common validation tasks. Of course, you can create new custom validators by inheriting from the base ValidationAttribute class.

Walkthrough: Using the Data Annotation Validators in an MVC Application

In this section, I show how you can use the Data Annotation validators in the context of an MVC application. I’ll show you how to use the validators when building a Movie database application (see Figure 1).

Figure 1 – Movie database application

clip_image002

Let’s start with the model class that we want to validate. The Movie class represents a particular movie. The Movie class is contained in Listing 1.

Listing 1 – Movie.cs

using System.ComponentModel.DataAnnotations;

namespace Tip43.Models
{
    public class Movie
    {
        public int Id { get; set; }
        
        [Required(ErrorMessage="Movie title is required.")]
        [StringLength(8, ErrorMessage="Movie title cannot be longer than 8 characters")]
        public string Title { get; set; }

        [Required(ErrorMessage="Movie director is required")]
        public string Director { get; set; }

        [Required(ErrorMessage="Box office totals is required")]
        public decimal? BoxOfficeTotals { get; set; }
    }
}

The Movie class in Listing 1 represents four properties of a movie: Id, Title, Director, and BoxOfficeTotals. The Data Annotations Required attribute is applied to the Title, Director, and BoxOfficeTotals properties. The StringLength validator is applied to the Title property.

The controller in Listing 2 exposes three actions: Index(), Create(), and Create(FormCollection form). The Index() action displays all of the existing movies in the database within the Index view. The Create() method displays the form for creating a new movie. Finally, the Create(FormCollection form) method is responsible for validating the new movie and adding the movie to the database.

Listing 2 – Controllers\HomeController.cs

using System.Web.Mvc;
using Microsoft.Web.Mvc;
using Tip43.Models;
using System.ComponentModel.DataAnnotations;
using Tip43.MvcValidation;

namespace Tip43.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 (ValidationIssueException vex)
            {
                ViewData.ModelState.CopyValidationIssues(vex);
                return View("Create", movieToCreate);
            }
            catch
            {
                return View("Create", movieToCreate);
            }

            return RedirectToAction("Index");
        }
    }
}

Let’s examine the second Create() action in more detail. This action first creates a new instance of the Movie class. Next, the form fields submitted to the Create() action are applied to the new Movie class with the help of the UpdateModel() method (The UpdateModel() method is a standard method of the MVC controller class).

The controller uses a repository class to interact with the Movie database table. The Add() method is called on the repository class to add the new movie to the database. If the repository class raises a validation error when the Add() method is called then the Catch clause for the ValidationIssueException is executed. This Catch clause copies all of the validation issues from the ValidationIssueException to ModelState and the form for creating a new movie is displayed once again so the validation issues can be fixed.

The MovieRepository class is contained in Listing 3.

Listing 3 – Models\MovieRepository.cs

using System.Collections.Generic;
using System.Linq;
using LinqToSqlExtensions;
using MvcFakes;
using Tip43.MvcValidation;

namespace Tip43.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)
        {
            Validation.ValidateAttributes(movieToAdd);
            _dataContext.Insert(movieToAdd);
            return movieToAdd;
        }

    }
}

The MovieRepository class exposes two methods: SelectAll() and Add(). The SelectAll() method is called by the Index() action to display all of the existing movies in the database. The Add() method validates the movie being added and inserts the new movie into the database with the help of the LINQ to SQL DataContext class.

The new movie is validated by calling the Validation.ValidateAttributes() method. This method executes each of the validators on the Movie class. For example, the method executes the Required validators on the Movie class Title, Director, and BoxOfficeTotals properties. If any of these properties do not have a value, then the Validate() method throws a ValidationIssueException.

You don’t need to do anything special in an MVC view when taking advantage of the Data Annotation validators. The view in Listing 4 uses the Html.ValidationSummary() helper to display the validation error messages. The Html.TextBox() helper adds a CSS class named input-validation-error to an input field automatically when the input field is associated with a property that has a validation error. The view in Listing 4 highlights input fields associated with a validation error with a red border (see Figure 2).

Listing 4 – Views\Home\Create.aspx

<%@ Page Language="C#" AutoEventWireup="true" CodeBehind="Create.aspx.cs" Inherits="Tip43.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>

Figure 2 – CSS applied to form fields with validation errors

clip_image002[1]

Summary

The Data Annotations validation attributes provide you with a very easy method of performing model-driven validation in an MVC application. For complex validation scenarios, I would recommend taking advantage of the Microsoft Enterprise Validation Application Block. For simple validation scenarios, such as a Movie database application, the Data Annotation validation attributes provide an easier alternative.

One other advantage of the Data Annotation validators is that using these validators makes it easier to transition a standard MVC application to a Dynamic Data MVC application. It is worth remembering that the very same set of validators is used in ASP.NET Dynamic Data applications.

Download the Code

11 Comments

  • I'm just curious why these validators are "easier" than the Validation Application Block. Also, what makes the VAL better at handling complex validation scenarios? I'm trying to decide between the two and am curious as to your thoughts.

  • Stephen - what about globalization? You have error messages inserted into attributes. Is this possible to use more flexible form of this texts?

  • So could an interface IValidationProvider.ValidateAttributes(entity) come in handy, to implement in a repository using any custom validation library? Not sure whether that kind of thing exists yet in MVCContrib.

  • This post makes it seem the methods you are calling are part of the framework but I'm not finding them anywhere.

    I've added a reference to System.ComponentModel.DataAnnotations but I'm not seeing "ValidationIssueException" or "Validation.Validate", "ValidationIssueException".

    Also, I'm using MVC Preview 5, which is the latest (to the best of my knowledge)... but I don't have the "ViewData.ModelState.CopyValidationIssues" method.

    If this is some custom code that you have made, could you make that more explicit in the article? If this is part of the framework, could you make that more explicit?

    For example, if the "ViewData.ModelState.CopyValidationIssues" is an extension method you created, put a comment above that and say so.

    If that method is there, but you have to add a reference to get it... put that in a comment right above that method call as well.

    People look to asp.net weblogs (and particularly of Microsoft employees) as a subject-matter authority. So clearly distinguishing between what is part of the framework and what is personal code is very important.

    I would hate for someone to ask me about validation in MVC when doing a presentation and for me to misrepresent what is in the framework itself.

    Thanks,
    -Timothy

  • Does the UpdateModel() method use the Data Annotations Validators to test for datatype mismatches?

    It is a little confusing because your first post on validation was validating against the forms collection data and subsequent posts started validating against the model itself. In fact you stated in that first post that the very reason not to validate against the model itself was because you would not be able to catch data type mismatches (typing in ABC when field was expecting a date).

  • @Mark -- I got this point wrong in my first post. The UpdateModel() method _does_ handle datatype mismatch issues. The sample code in this tip uses both the UpdateModel() method and the Data Annotation validators to do validation. Mismatch issues are handled by the UpdateModel() method.

    Download the code and try it. You get a useful error message back when you enter the string apple for the BoxOfficeTotals property.

  • @Nullable (or @Timothy) -
    Check to make sure that you have .Net 3.5 SP1
    This has nothing to do with the MVC framework and has everything to do with changes made in the service pack

  • Mismatch issues are handled by the UpdateModel() method.......You get a useful error message back when you enter the string apple for the BoxOfficeTotals property

    But does the developer have the ability to customize the message that the user sees for data type mismatch errors?

    thanks

  • Great post! It didn't even realize that the Dynamic Data validation annotations were available ...

    That said, it looks like a lot of the work is done by your Validation class (along with ValidationIssue and ValidationIssueException) and the CopyValidationIssues() extension method, which weren't really explained in your post.

    I'm going to try adapting some of this to a WPF application I'm working on. Thanks!

  • Great article.
    I implemented successfully the DataAnnotations attributes + Validation in my current Project.
    Question:
    I'm trying to put all validation messages in Resource file and don't find the correct syntax (no example in msdn).
    Can You show an example usage for Resource files to display the DataAnnotations attributes messages?

  • Modification of code:
    1) First you have to import the namespace to Create.ASPX otherwise you will get compilation errors when you are rendering this page.
    2)You must add using System.Collections; in the namespaces for the HomeController.cs
    3)You can revise this code as show below for the public ActionResult Create Method
    [AcceptVerbs("POST")]
    public ActionResult Create(ICollection form)
    {
    var movieToCreate = new Movie();

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

    return RedirectToAction("Index");
    }
    }
    }
    After making these modifications, the project succeeded wihout any errors.
    I hope this helps. Thank You

Comments have been disabled for this content.