Custom Validation with Data Annotations

Mirza Farrukh Ali
2 min readDec 2, 2017

--

To create customized validation checks which is required in scenarios like Cross-property validation i.e, when we need to validate the content of a property in light of the value stored in another property.

You can either create a class that derives from the ValidationAttribute class or create a method that performs the validation check and reference that method when applying the CustomValidationAttribute to the data member.

CustomValidationAttribute:

Uses a custom method for validation. You apply the CustomValidationAttribute attribute to an entity or one of its members when you need to specify a method to use for validating the entity or the value of the member.

[CustomValidation(typeof(MemoDocument), “ValidateCategoryAndPriority”)]
public Categories Category { get; set; }

The CustomValidation attribute takes two parameters: a type and a method name.The method must be public and static with any of the following signatures:

public static ValidationResult ValidateCategoryAndPriority(Categories category)
public static
ValidationResult ValidateCategoryAndPriority(Categories category, ValidationContext context)

Using the first overload is the same as defining custom validation logic for an individual value. It is sort of equivalent to creating a custom data annotations attribute — it’s quicker to write but more rigid as far as the signature is concerned. Much more interesting is the second overload. In fact, through the ValidationContext parameter, you can get a reference to the model object and check as many properties as you like.

public static ValidationResult ValidateCategoryAndPriority(Categories category, ValidationContext context){
// Grab the model instance
var memo = context.ObjectInstance as MemoDocument;
if (memo == null)
throw new NullReferenceException();
// Cross-property validation
if (memo.Category == Categories.Personal && memo.Priority > 3)
return new ValidationResult(“Category and priority are not consistent.”);
return ValidationResult.Success;
}

Custom Validation Annotations:

When you create a class that derives from ValidationAttribute, override the IsValid method to provide the logic for your customized validation check. The logic to determine if a value is valid is implemented in the overridden IsValid method. The Validate method calls the IsValid method and throws a ValidationException if the value is not valid.

public class MaxWordsAttribute:VaildationAttribute {
private readonly int _maxwords;
public MaxWordsAttribute(int maxWords):base (“{0} has too many words”){
_maxwords=maxWords;
}
protected override ValidationResult
IsValid(object value,ValidationContext validationContext){
// access other properties of the model with ValidationContext
var errorMessage=
FormatErrorMessage(validationContext.DisplayName)
//
return new ValidationResult(errorMessage) or
//
return ValidationResult.Success
}
}
[MaxWords(10,ErrorMessage=”There are too many words in {0}”)]
public string LastName{get;set;}

Self-validation:

A self-validating model is a model object that knows how to validate itself.
Model implements the IValidatableObject by overriding validate method

public ActionResult Edit(Order order) {
if (!Validate(order))
ModelState.AddModelError(…);

}
public class Order:IValidatableObject {
public IEnumerable<ValidationResult> Validate(ValidationContext validationContext){
if(LastName!=null&& LastName.Split(‘ ’).Length>10){
yield return new ValidationResult(“The last name has too many words”,new[]{“LastName”});//associate error with field name
}
}
}

The return type for validate is an IEnumerable<ValidationResult> instead of single ValidationResult,because the logic may validate other properties as well, thus may return multiple validation errors.

--

--