Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET MVC 3 Data Annotation: Add validation dynamically

I'm new with data annotation. I'd like to know if it possible (and how) to add some validation dynamically. It is very extensive to explain why, but I've a ViewModel that receives and object when created. In that object I must check for some property and depending its value I should have or not some validations.

An example:

public class ProfileViewModel
{
    [Required(ErrorMessage = "The field {0} is required")]
    [Display(Name = "Client Code")]
    public int ClientCode { get; set; }

    [Required(ErrorMessage = "The field {0} is required")]
    [StringLength(100, ErrorMessage = "The field {0} must have up to 100 characters.")]
    [Display(Name = "Company")]
    public string Company { get; set; }

    [StringLength(50, ErrorMessage = "The field {0} must have up to 50 characters.")]
    [Display(Name = "Name")]
    public string Name { get; set; }

    [StringLength(50, ErrorMessage = "The field {0} must have up to 50 characters.")]
    [Display(Name = "LastName")]
    public string LastName { get; set; }

    public ProfileViewModel(User usr)
    {
        if (usuario.ClientCode != null)
        {
            ClientCode = Convert.ToInt32(usr.ClientCode);
        }
        else
        {
             //ClientCode and Company are not yet required.
             //Name and LastName are now required.
        }
        Company = usr.Company;
        Name = usr.Name;
        LastName = usr.LastName;
    }
}
like image 887
Diego Avatar asked May 24 '11 10:05

Diego


3 Answers

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

like image 174
Diego Avatar answered Oct 07 '22 22:10

Diego


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; }
like image 5
Stef Heyenrath Avatar answered Oct 07 '22 22:10

Stef Heyenrath


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

like image 1
endorphin Avatar answered Oct 07 '22 21:10

endorphin