Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom model validation of dependent properties using Data Annotations

Since now I've used the excellent FluentValidation library to validate my model classes. In web applications I use it in conjunction with the jquery.validate plugin to perform client side validation as well. One drawback is that much of the validation logic is repeated on the client side and is no longer centralized at a single place.

For this reason I'm looking for an alternative. There are many examples out there showing the usage of data annotations to perform model validation. It looks very promising. One thing I couldn't find out is how to validate a property that depends on another property value.

Let's take for example the following model:

public class Event {     [Required]     public DateTime? StartDate { get; set; }     [Required]     public DateTime? EndDate { get; set; } } 

I would like to ensure that EndDate is greater than StartDate. I could write a custom validation attribute extending ValidationAttribute in order to perform custom validation logic. Unfortunately I couldn't find a way to obtain the model instance:

public class CustomValidationAttribute : ValidationAttribute {     public override bool IsValid(object value)     {         // value represents the property value on which this attribute is applied         // but how to obtain the object instance to which this property belongs?         return true;     } } 

I found that the CustomValidationAttribute seems to do the job because it has this ValidationContext property that contains the object instance being validated. Unfortunately this attribute has been added only in .NET 4.0. So my question is: can I achieve the same functionality in .NET 3.5 SP1?


UPDATE:

It seems that FluentValidation already supports clientside validation and metadata in ASP.NET MVC 2.

Still it would be good to know though if data annotations could be used to validate dependent properties.

like image 248
Darin Dimitrov Avatar asked Feb 17 '10 12:02

Darin Dimitrov


People also ask

How do you validate data annotations?

DataAnnotations namespace includes the following validator attributes: Range – Enables you to validate whether the value of a property falls between a specified range of values. RegularExpression – Enables you to validate whether the value of a property matches a specified regular expression pattern.

Can we do validation in MVC using data annotations?

ASP.NET MVC provides a unique feature in which we can validate the models using the Data Annotation attribute.

What is data annotation validator attributes in MVC?

Data annotation attributes are attached to the properties of the model class and enforce some validation criteria. They are capable of performing validation on the server side as well as on the client side. This article discusses the basics of using these attributes in an ASP.NET MVC application.


2 Answers

MVC2 comes with a sample "PropertiesMustMatchAttribute" that shows how to get DataAnnotations to work for you and it should work in both .NET 3.5 and .NET 4.0. That sample code looks like this:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)] public sealed class PropertiesMustMatchAttribute : ValidationAttribute {     private const string _defaultErrorMessage = "'{0}' and '{1}' do not match.";      private readonly object _typeId = new object();      public PropertiesMustMatchAttribute(string originalProperty, string confirmProperty)         : base(_defaultErrorMessage)     {         OriginalProperty = originalProperty;         ConfirmProperty = confirmProperty;     }      public string ConfirmProperty     {         get;         private set;     }      public string OriginalProperty     {         get;         private set;     }      public override object TypeId     {         get         {             return _typeId;         }     }      public override string FormatErrorMessage(string name)     {         return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,             OriginalProperty, ConfirmProperty);     }      public override bool IsValid(object value)     {         PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);         object originalValue = properties.Find(OriginalProperty, true /* ignoreCase */).GetValue(value);         object confirmValue = properties.Find(ConfirmProperty, true /* ignoreCase */).GetValue(value);         return Object.Equals(originalValue, confirmValue);     } } 

When you use that attribute, rather than put it on a property of your model class, you put it on the class itself:

[PropertiesMustMatch("NewPassword", "ConfirmPassword", ErrorMessage = "The new password and confirmation password do not match.")] public class ChangePasswordModel {     public string NewPassword { get; set; }     public string ConfirmPassword { get; set; } } 

When "IsValid" gets called on your custom attribute, the whole model instance is passed to it so you can get the dependent property values that way. You could easily follow this pattern to create a date comparison attribute, or even a more general comparison attribute.

Brad Wilson has a good example on his blog showing how to add the client-side portion of the validation as well, though I'm not sure if that example will work in both .NET 3.5 and .NET 4.0.

like image 75
Travis Illig Avatar answered Oct 11 '22 21:10

Travis Illig


I had this very problem and recently open sourced my solution: http://foolproof.codeplex.com/

Foolproof's solution to the example above would be:

public class Event {     [Required]     public DateTime? StartDate { get; set; }      [Required]     [GreaterThan("StartDate")]     public DateTime? EndDate { get; set; } } 
like image 24
Nick Riggs Avatar answered Oct 11 '22 22:10

Nick Riggs