ASP.NET MVC 3 Data Annotation: Add validation dynamically
I think that the simplest way of doing what I wanted is implementing IValidatableObject
:
public class Product : IValidatableObject{ public int Prop1 { get; set; } public int Prop2 { get; set; } public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) { if (Prop1 < Prop2) yield return new ValidationResult("Property 1 can't be less than Property 2"); }}
See also: Class-Level Model Validation with ... ASP.NET MVC 3
Custom Attribute:
[AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited = true)]public class CustomRequiredIfAttribute : CustomAttribute{ private RequiredAttribute innerAttribute = new RequiredAttribute(); public string DependentProperty { get; set; } public object TargetValue { get; set; } public CustomRequiredIfAttribute() { } public CustomRequiredIfAttribute(string dependentProperty, object targetValue) : base() { this.DependentProperty = dependentProperty; this.TargetValue = targetValue; } public override bool IsValid(object value) { return innerAttribute.IsValid(value); }}
Custom RequiredIfValidator
using System;using System.Collections.Generic;using System.Web.Mvc;namespace Custom.Web.Validation{ public class RequiredIfValidator : DataAnnotationsModelValidator<CustomRequiredIfAttribute> { public RequiredIfValidator(ModelMetadata metadata, ControllerContext context, CustomRequiredIfAttribute attribute) : base(metadata, context, attribute) { } public override IEnumerable<ModelClientValidationRule> GetClientValidationRules() { return base.GetClientValidationRules(); } public override IEnumerable<ModelValidationResult> Validate(object container) { // get a reference to the property this validation depends upon var field = Metadata.ContainerType.GetProperty(Attribute.DependentProperty); if (field != null) { // get the value of the dependent property object value = field.GetValue(container, null); // compare the value against the target value if (this.IsEqual(value) || (value == null && Attribute.TargetValue == null)) { // match => means we should try validating this field if (!Attribute.IsValid(Metadata.Model)) { // validation failed - return an error yield return new ModelValidationResult { Message = ErrorMessage }; } } } } private bool IsEqual(object dependentPropertyValue) { bool isEqual = false; if (Attribute.TargetValue != null && Attribute.TargetValue.GetType().IsArray) { foreach (object o in (Array)Attribute.TargetValue) { isEqual = o.Equals(dependentPropertyValue); if (isEqual) { break; } } } else { isEqual = Attribute.TargetValue.Equals(dependentPropertyValue); } return isEqual; } }}
Register custom DataAnnotationsModelValidatorProvider
DataAnnotationsModelValidatorProvider.RegisterAdapter(typeof(CustomRequiredIfAttribute), typeof(RequiredIfValidator));
Use this CustomRequiredIf in the ViewModel
[CustomRequiredIf("CategoryId", 3, ErrorMessageResourceName = GlobalResourceLiterals.AccountGroup_Required)]public string AccountGroup { get; set; }
Heres the updated MVC 3 version of that blog post http://blogs.msdn.com/b/simonince/archive/2011/02/04/conditional-validation-in-asp-net-mvc-3.aspx