Using DataAnnotations for Validation

Valid for the following versions: ASP.NET MVC 2

Though the DataAnnotations attributes were usable in the first version of ASP.NET MVC, how they are used in version 2 is a bit different. This tutorial is specific to version 2.

In the last tutorial we saw the more basic, more manual way of doing validation in ASP.NET MVC. Doing it that way works fine but ultimately becomes cumbersome. Here we will see another way of validation that is better most of the time, and that is to use the DataAnnotations attributes to markup the view model for a page.

The Origins of DataAnnotations

The DataAnnotations were released with the ASP.NET Dynamic Data project and .NET 3.5. They live in the System.ComponentModel.DataAnnotations namespace in the assembly by the same name. If you want to use them, reference the assembly and namespace and you should be good to go.

Though they were released for the Dynamic Data project, they apparently appealed to the MVC team as something useful for that framework as well. In version 1 of MVC they could be used but in version 2 they have become a bit more baked in.

Using DataAnnotations

Using the DataAnnotations is very straightforward. Attach the attributes to the appropriate properties in your view model and then...nothing else. That is it. The rest is handled by the framework.

As an example, let us change the view model and controller code from our last example. First, we add the RequiredAttribute on the "MonkeyName" property with the error specified and the RangeAttribute, with the appropriate arguments, to the age property, like so:

    using System;
    using System.ComponentModel.DataAnnotations;

    namespace SomeNamespace
    {
        public class MonkeyViewModel
        {
            [Required(ErrorMessage = "Please enter a name.")]
            public string MonkeyName { get; set; }

            [Range(0, 50, ErrorMessage = "The age of the monkey cannot be greater than 50.")]
            public int? Age { get; set; }
        }
    }
    

One nice thing about this is that the thing to be validated and the validation metadata about it is together in one spot. This gets a +1 for readability. Next we get to delete the validation logic out of the post method on the controller. Now it is doing what I want it to do, which is only control flow.

        [HttpPost]
        public ActionResult CreateANewMonkey(MonkeyViewModel vm)
        {
            if (ModelState.IsValid)
                return RedirectToAction("SomeConfirmationScreenOrSomething");

            return View(vm);
        }
    

How Does That Work?

What voodoo is it that is making that work? The awesome voodoo is a function of the DefaultModelBinder. This was mentioned in our short discussion on that class. One of its jobs is to look for validation attributes on the type it constructs for you when the method is invoked on the controller.

And though it may be obvious, it is important to go ahead and point out that by the time the controller method executes, the validation has already happened. If you debug the method and check the ModelState.IsValid flag, assuming there is a validation error, you will find that it is already set to false. When manual validation is also necessary, that can still be done afterwards, in the controller or wherever you want to put it.

Awesome Side Effect

So this changes some things. A positive side effect is that it is going to push you towards separating the view model from the model, which I think is a good idea anyway. If you use your domain model directly in your views, and you use that same object in more than one, you can find yourself in a pickle if the validation logic needs to be different between the two. Instead, do yourself a favor give the views their own view models.

Less Cool Side Effect

If you were to run the unit test that we wrote in the last tutorial, you would find that it fails. Though it may not be obvious at first, this actually makes sense. It is the DefaultModelBinder that is executing the validation and in that unit test the model binding process is being skipped entirely. Ergo, there is nothing to mark the state as invalid.

Is this a problem? I am on the fence on this one (though leaning towards the negative), but Brad Wilson of the ASP.NET team does not think so (in a fantastic blog post). Perhaps more on this later.

The Available DataAnnotations

Two validation attributes were used above, the RequiredAttribute and the RangeAttribute. There is also a StringLengthAttribute and a RegularExpressionAttribute that you can use to validate the length of a string or, in the case of the latter, validate the value based on any regular expression.

As an FYI, there are other annotation attributes that are used by the ASP.NET MVC framework as well, such as the UIHintAttribute and the DisplayNameAttribute in the parent System.ComponentModel namespace. Since these are not used for validation, they will be discussed at a later time.

Speak Your Mind!

Have something to say? Find a grammatical mistake? Think I said something incorrect? Don't like my perspective? Hate my color scheme? Whatever it is, you can let me know. I would appreciate it if you did.