Jul 4, 2010

ASP.NET MVC basic validation with DataAnnotations

I am currently working on an MVC 2 project and we need basic validation for our models. An easy way to accomplish this is to use the attributes in the System.ComponetModel.DataAnnotations namespace. This works out of the box with no additional dependecies. For basic model validation you end up with something like this:

    public class LoginModel
    {
        [Required(AllowEmptyStrings = false, ErrorMessage = "Email address is required"),
         RegularExpression("@"^([a-zA-Z0-9]+[a-zA-Z0-9._%-]*@(?:[a-zA-Z0-9-]+\.)+[a-zA-Z]{2,4})$"", ErrorMessage = "Email address is invalid.")]
        public string EmailAddress { get; set; }

        public string Password { get; set; }
        public bool RemeberMe { get; set; }
    }

It looks a little clunky if you have a lot of attributes decorating one property, but for us it’s the simplest thing that works for now. Now we need a way to test this. My first instinct was to call Validator.TryValidate() on my model and get back the list of broken validation rules. To do this we need to first construct a validation context. The first tests looks like this:

        [Test]
        public void should_validate_given_a_model_with_a_missing_email_address()
        {
            var model = new LoginModel {EmailAddress = "", Password = "password", RemeberMe = false };
            
            var errors = ValidateModel(model);

            Assert.That(1.Equals(errors.Count()));
            Assert.IsNotNull(errors.Where(x => x.ErrorMessage.Contains("Email address is required")));
        }

        private IEnumerable<ValidationResult> ValidateModel(LoginModel model)
        {
            var results = new List<ValidationResult>();
            var context = new ValidationContext(model, null, null);
            Validator.TryValidateObject(model, context, results, true);

            return results;
        }

We needed to validate a few different models so we extracted the ValidateModel method into a base class, ValidationTestBase. The ValidateModel method is made generic so it can operate on any given model. The base class looks like this:

    public class ValidationTestBase
    {
        protected IEnumerable<ValidationResult> ValidateModel<T>(T model)
        {
            var results = new List<ValidationResult>();
            var context = new ValidationContext(model, null, null);
            Validator.TryValidateObject(model, context, results, true);

            return results;
        }
    }

In practice this works pretty well, and having this area well covered ensures no validation issues sneak up on us. You can easily implement a custom validator if you need something that is not provided out of the box. We had some debates around whether we should do more complex validations the same way or not. And easy example that comes to mind is check the database to determine if a user name is already taken. We decided that is better left to a service layer, and rely on the attributes only for basic data validation.

There is an equivalent open source solution to the DataAnnotations namespace – Castle.Components.Validator. The functionality provided is largely equivalent, although we didn’t explore it in detail. In a future post I will cover how to link the DataAnnotations to client side validation.