MSDN UK Team blog

Get all the latest Microsoft developer news, tools, downloads, tutorials and tips right here, every day.
Search Form
Unfiltered HTML
Developers code with microsoft

MSDN Flash technical article - Extending ASP.NET MVC’s Validation

Unfiltered HTML
Unfiltered HTML

MSDN Flash technical article - Extending ASP.NET MVC’s Validation

  • Comments 2

This week’s MSDN Flash feature article is all about enhancing the validation support in ASP.NET MVC using the specific example of cross-field validation. We’d like to thank David Bending for writing the article which is a little more “code-heavy” than a typical MSDN Flash article. What do you think? Would you prefer to see more of this style of article? Or maintain the status quo? Let us know by either contacting Mike Ormond here or sending him a tweet @MikeOrmond.

And of course if you have ideas for Flash articles you’d like to see or polls you’d like, er, “polled” or you have an idea for an article you’d like to write yourself then let me know as well. Now over to David…

Nearly all web applications require some form of validation. Validation performs two main purposes: helping the user to enter correct values on a web page, and protecting the application from invalid or malicious input.

Validation can take place client-side within the browser, or server-side when the page is posted. ASP.NET MVC2 comes with a flexible framework for applying validation using data annotations on the view model. Scott Guthrie has written an excellent tutorial on basic MVC validation here.

Limitations of Built-in MVC Validation

The built-in validation works well for standard scenarios, but there are a few common scenarios it can’t cope with. One of the main issues is that validation can only be done on a model property in isolation. Often we will want to validate a property based on the current value of some other property: for instance we may want our confirm password field value to be the same as the password box.

In this article we will extend the MVC validation framework to provide support for cross-field validation. We’ll do this in part by creating custom validation attributes as described in Phil Haack’s article but we’ll also need to extend the validator framework to allow us to validate across fields.

Extending the Framework to Support Cross-Field Validation

To create a custom validation attribute we usually derive from ValidationAttributeand override the IsValid method, however IsValid only gets passed the value of the property to validate and we are going to need to see the value of other fields in the model. To accommodate this we’ll create an interface ICrossFieldValidationAttribute that has an IsValid method with access to the entire view model (note these code snippets only show significant lines – download the example solution to get the whole files):

view sourceprint?

1.public interface ICrossFieldValidationAttribute

2.{

3. bool IsValid(ControllerContext controllerContext, 

4. object model, 

5. ModelMetadata modelMetadata);

6.}

Next we build a base class for cross-field validators. This base class uses the extended IsValid defined above, rather than the narrower method used in the framework’s DataAnnotationsModelValidator class.

view sourceprint?

01.public abstract class CrossFieldValidator<TAttribute> :

02. DataAnnotationsModelValidator<TAttribute>

03. where TAttribute : ValidationAttribute, ICrossFieldValidationAttribute

04.{

05. public override IEnumerable<ModelValidationResult> Validate(object container)

06. {

07. var attribute = Attribute as ICrossFieldValidationAttribute;

08.

09. if (!attribute.IsValid(ControllerContext, container, Metadata))

10. {

11. yield return new ModelValidationResult { Message = ErrorMessage };

12. }

13. }

14.}

Building the Validation

Now that we have a cross-field validation framework in place, we can build our new attribute.

view sourceprint?

01.[AttributeUsage(

02. AttributeTargets.Property |

03. AttributeTargets.Field, AllowMultiple = false)]

04.public class EqualToPropertyAttribute :

05. ValidationAttribute, ICrossFieldValidationAttribute

06.{

07. public string OtherProperty { get; set; }

08.

09. public bool IsValid(ControllerContext controllerContext, 

10. object model, ModelMetadata modelMetadata)

11. {

12. var propertyInfo = model.GetType().GetProperty(OtherProperty);

13. var otherValue = propertyInfo.GetGetMethod().Invoke(model, null);

14.

15. if (modelMetadata.Model == null) modelMetadata.Model = string.Empty;

16. if (otherValue == null) otherValue = string.Empty;

17.

18.

19. return modelMetadata.Model.ToString() == otherValue.ToString();

20. }

21.}

And apply it to the view model:

view sourceprint?

1.public class Account

2.{

3. public string Password { get; set; }

4.

5. [EqualToProperty(OtherProperty = "Password")]

6. public string ConfirmPassword { get; set; }

7.}

We also need to create a validator class to emit the correct client-side rules, and to invoke the IsValid method.

view sourceprint?

01.public class EqualToPropertyValidator : 

02. CrossFieldValidator<EqualToPropertyAttribute>

03.{

04. public override IEnumerable<ModelClientValidationRule> GetClientValidationRules()

05. {

06. var rule = new ModelClientValidationRule

07. {

08. ValidationType = "equaltoproperty",

09. ErrorMessage = Attribute.FormatErrorMessage(Metadata.PropertyName),

10. };

11.

12. rule.ValidationParameters.Add("otherProperty", Attribute.OtherProperty);

13.

14. return new[] { rule };

15. }

16.}

Finally we need to create a JavaScript function to evaluate the rule client-side:

view sourceprint?

01.jQuery.validator.addMethod("equaltoproperty", function (value, element, params) {

02. if (this.optional(element)) {

03. return true;

04. }

05.

06. var otherPropertyControl = $("#" + params.otherProperty);

07. if (otherPropertyControl == null) {

08. return false;

09. }

10.

11. var otherPropertyValue = otherPropertyControl[0].value;

12. return otherPropertyValue == value;

13.});

14.

15.function testConditionEqual(element, params) {

16. var otherPropertyControl = $("#" + params.otherProperty);

17. if (otherPropertyControl == null) {

18. return false;

19. }

20.

21. var otherPropertyValue;

22. if (otherPropertyControl[0].type == "checkbox") {

23. otherPropertyValue = (otherPropertyControl[0].checked) ? "True" : "False";

24. } else {

25. otherPropertyValue = otherPropertyControl[0].value;

26. }

27.

28. return otherPropertyValue == params.comparand;

29.}

And the final step is to register our new attribute with MVC. We do this in Global.asax Application_Start method like this:

view sourceprint?

1.DataAnnotationsModelValidatorProvider.RegisterAdapter(

2. typeof(EqualToPropertyAttribute),

3. typeof(EqualToPropertyValidator));

Next Steps

Using the framework we’ve implemented here we can develop validation as complex as we need. Everything is testable using unit tests for the server-side validation and Visual Studio 2010 coded UI tests for the client-side validation.

Other approaches you could take would be to use the xVal validation framework from CodePlex, or download the preview of MVC 3 that contain a cross-field validation framework in the box, using a technique very similar to this article.

About the Author

clip_image002David Bending is a Technical Lead at Confused.com, the UK’s first insurance price comparison website. David specialises in Agile development on the .NET platform and blogs about MVC, Agile and other topics at Davepoint.

  • Thank for the great article. We are using MVC 3 Preview (just upgraded to Beta) so Im interested in the "cross-field validation framework in the box" you mentioned. Where can i find more information on it. I can't seem to find alot of information out there on it.

    Thanks in advance.

  • Hi Aaron

    This post on Scottgu's blog gives more info :-)

    weblogs.asp.net/.../introducing-asp-net-mvc-3-preview-1.aspx

    Thanks

    Rachel

Page 1 of 1 (2 items)
Leave a Comment
  • Please add 2 and 1 and type the answer here:
  • Post