ASP.NET MVC 3 Data Annotations Provide Property-Level Contingent Validation

Published on Author nickriggs9 Comments

About a year ago I introduced the open source project Foolproof Validation for the purpose of adding contingent model-aware data annotation validation to ASP.NET MVC. I’m happy to say that ASP.NET MVC

3 will address the problem out of the box.

Pre MVC 3, if you needed an annotation to be model-aware, meaning that it needed knowledge of more than one property, you had to place that annotation on the class instead of property. This resulted in some undesirable effects, most notably that the model would fail validation instead of the particular property. Take for example PropertiesMustMatchAttribute in the stock MVC 2 application: It was placed on the ChangePasswordModel and RegisterModel classes, not the ConfirmPassword property of the models. This resulted in the validation messages being displayed in the summary and not beside the input box:

Enter MVC 3’s stock application. Notice they have created a custom CompareAttribute and instead of annotating the class, they annotate the ConfirmPassword property:

[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }

Which results in:

Much better! The magic in the CompareAttribute is in the IsValid function:

protected override ValidationResult IsValid(object value, ValidationContext context)
    var confirmValue = context.ObjectType.GetProperty(ConfirmProperty).GetValue(context.ObjectInstance, null);
    if (!Equals(value, confirmValue))
        return new ValidationResult(FormatErrorMessage(context.DisplayName));
    return null;

So the context parameter is your model. It uses reflection to get value of the property it’s comparing against, and then Equals is called using both property values. If Equals fails, a ValidationResult is returned with the error message.

This is a great addiction to the MVC framework. I haven’t completely decided how this will affect Foolproof in the future. Foolproof still provides a lot of value by providing several out of the box annotations that come up quite frequently, such as [GreaterThan] and [RequiredIf] – along with accompanying JavaScript validation. I suspect a fork will be in order, and the MVC 3 fork will utilize the new architecture while still providing a set of useful annotations and JavaScript.

9 Responses to ASP.NET MVC 3 Data Annotations Provide Property-Level Contingent Validation

  1. Thanks for this blog post. I’m using the MVC 3 RC as my first introduction to MVC and I was getting stuck on this. The model level validation works but doesn’t seem very elegant. This is much better.

    I did try to use Foolproof in my project, but it doesn’t seem to work with MVC 3. I think it has something to do with the new HTML 5 attributes only being allowed to be lowercase. I tried to open up the latest source code for Foolproof to see if I could fix it, but VS kept locking up when I tried to open the solution/project files.

    So I think I’ll just wait for your new version. But thanks for your efforts!

  2. Hi Nick,

    Foolproof is awesome and handy! We’ve invested a lot of time on building an application with MVC2, and we are intending to continue using Foolproof. Could you help us with a problem I have encountered:

    I have a function to help me do explicit validation of properties using reflection(code pasted below – hopefully it formats ok! ). Even though after model binding, ModelState is valid and all of the stock Validation attributes are passing this function and returning true in the end ( Required, Range etc…), when I add foolproof validation attributes to properties, this function fails validation despite ModelState being valid.

    Any clues?

    public static bool Validate(T model)
    	var valid = true;
    	var properties = model.GetType().GetProperties();
    	foreach (var propertyInfo in properties)
    		var attributes = propertyInfo.GetCustomAttributes(false).OfType();
    		foreach (var attribute in attributes)
    				var objPropInfo = model.GetType().GetProperty(propertyInfo.Name);
    				attribute.Validate(objPropInfo.GetValue(model, null), propertyInfo.Name);
    			catch (Exception)
    				valid = false;
    		if (!valid) break;
    	return valid;
  3. Sorry, after reading my own comment, I thought it needed to be better worded:

    I have a function to help me do explicit validation of properties using reflection(code in the above comment).

    The normal scenario is this:

    When a request is posted to my action with my model, the model binding validates the model based on the data annotations. Lets say my ModelState is valid. In this case, when I call the my custom function above, it returns true as it should.

    Enter Foolproof. Now, I have a few properties with Foolproof RequiredIfTrue attribute. Now, when the request is posted to my action, the model binding kicks in. Let us say my ModelState is valid. In this case though, when I call my custom function above, it returns false. This happens, despite the fact that ModelState is valid.

    Please help.

  4. Attractive section of content. I just stumbled upon your blog and in accession
    capital to assert that I get actually enjoyed account
    your blog posts. Anyway I’ll be subscribing to your augment and even I achievement you access consistently rapidly.

  5. I’m impressed, I have to admit. Rarely do I encounter a blog that’s both educative and interesting,
    and let me tell you, you’ve hit the nail on the head. The issue is something that too few men and women are speaking intelligently about. Now i’m very happy that
    I came across this during my hunt for something regarding this.

  6. Wonderful blog! Do you have any suggestions for aspiring
    writers? I’m hoping to start my own blog soon but I’m a little lost on everything.
    Would you propose starting with a free platform like WordPress or go
    for a paid option? There are so many choices out there that I’m totally confused .. Any tips? Many thanks!

Leave a Reply

Your email address will not be published. Required fields are marked *