Custom Validation Attributes: Comparing Two Properties in the Same Model

Custom Validation Attributes: Comparing two properties in the same model

You can create a custom validation attribute for comparison two properties. It's a server side validation:

public class MyViewModel
{
[DateLessThan("End", ErrorMessage = "Not valid")]
public DateTime Begin { get; set; }

public DateTime End { get; set; }
}

public class DateLessThanAttribute : ValidationAttribute
{
private readonly string _comparisonProperty;

public DateLessThanAttribute(string comparisonProperty)
{
_comparisonProperty = comparisonProperty;
}

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
ErrorMessage = ErrorMessageString;
var currentValue = (DateTime)value;

var property = validationContext.ObjectType.GetProperty(_comparisonProperty);

if (property == null)
throw new ArgumentException("Property with this name not found");

var comparisonValue = (DateTime)property.GetValue(validationContext.ObjectInstance);

if (currentValue > comparisonValue)
return new ValidationResult(ErrorMessage);

return ValidationResult.Success;
}
}

Update:
If you need a client side validation for this attribute, you need implement an IClientModelValidator interface:

public class DateLessThanAttribute : ValidationAttribute, IClientModelValidator
{
...
public void AddValidation(ClientModelValidationContext context)
{
var error = FormatErrorMessage(context.ModelMetadata.GetDisplayName());
context.Attributes.Add("data-val", "true");
context.Attributes.Add("data-val-error", error);
}
}

The AddValidation method will add attributes to your inputs from context.Attributes.

Sample Image

You can read more here IClientModelValidator

C# Custom Validation Attributes: Comparing two Dates in the same model

Validation attributes are great for validating a single property. I wouldn't attempt to validate multiple properties with attributes, since you can't be certain in what order the fields will be assigned and when the validation should occur.

Instead I would implement IValidatableObject;

public class YourClass: IValidatableObject
{
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (TerminationDate < OtherDate + TimeSpan.FromDays(3))
yield return new ValidationResult("... error message here ...");
}
}

Custom validation attribute that compares the value of my property with another property's value in my model class

Here's how you could obtain the other property value:

public class CustomAttribute : ValidationAttribute
{
private readonly string _other;
public CustomAttribute(string other)
{
_other = other;
}

protected override ValidationResult IsValid(object value, ValidationContext validationContext)
{
var property = validationContext.ObjectType.GetProperty(_other);
if (property == null)
{
return new ValidationResult(
string.Format("Unknown property: {0}", _other)
);
}
var otherValue = property.GetValue(validationContext.ObjectInstance, null);

// at this stage you have "value" and "otherValue" pointing
// to the value of the property on which this attribute
// is applied and the value of the other property respectively
// => you could do some checks
if (!object.Equals(value, otherValue))
{
// here we are verifying whether the 2 values are equal
// but you could do any custom validation you like
return new ValidationResult(this.FormatErrorMessage(validationContext.DisplayName));
}
return null;
}
}

Compare two properties with custom DataAnnotation Attribute?

I've tried placing only StartTime in the annotation with no luck. How
can I pass in a property value from the same model class?

That's impossible because attributes are metadata that is baked into the assembly at compile-time. This means that you can pass only CONSTANT parameters to an attribute. Yeah, that's a hell of a limitation because in order to perform such an obvious validation thing as comparing 2 values in your model you will have to write gazzilion of plumbing code such as what I have illustrated here for example: https://stackoverflow.com/a/16100455/29407 I mean, you will have to use reflection! Come on Microsoft! Are you serious?

Or just cut the crap of data annotations and start doing validation the right way: using FluentValidation.NET. It allows you to express your validation rules in a very elegant way, it greatly integrates with ASP.NET MVC and allows you to unit test your validation logic in isolation. It also doesn't rely on reflection so it is super fast. I have benchmarked it and using it in very heavy traffic production applications.

Data annotations just don't cut the mustard compared to imperative validation rules when you start writing applications that are a little more complicated than a Hello World and which require a little more complex validation logic than you would have in a Hello World application.

Using DataAnnotations to compare two model properties

There is a CompareAttribute in the ASP.NET MVC 3 Framework that does this. If you are using ASP.NET MVC 2 and targeting .Net 4.0 then you could look at the implementation in the ASP.NET MVC 3 source code.



Related Topics



Leave a reply



Submit