Search code examples
c#asp.net-mvcasp.net-mvc-3razorentity-attribute-value

How can I do a data type validation on a field that is dependent on another field?


I am having trouble performing data type validation which is dependent on another field. Most of the examples I found here are for making a field required or not based on a value of another field (MaidenName will be required only if IsMarried is true).

My Model

public class AttributeValuesModel
{
    public IList<AttributeModel> Values {get; set;}
}

public class AttributeModel
{
    [Required]
    public string AttributeName {get; set;}

    [Required]
    public string AttributeValue {get; set;}

    [Required]
    public int DataTypeId {get; set;}
}

What I would like to do is to validate the user input for AttributeValue based on the value of DataTypeId. For clarity, the value of DataTypeId is known before I even show the view to the user.

    //Possible values for DataTypeId are 
    //1 for decimal
    //2 for dates
    //3 for integer

Is this possible?


Solution

  • It's not so hard to roll your own validation attribute. I have implemented one some time ago. It checks whether value of other property is smaller than property that is decorated with this attribiute:

    [AttributeUsage(AttributeTargets.Property, AllowMultiple = true, Inherited=true)]
    public class SmallerThanAttribute : ValidationAttribute
    {
        public SmallerThanAttribute(string otherPropertyName)
        {
            this.OtherPropertyName = otherPropertyName;
        }
    
        public string OtherPropertyName { get; set; }
        public string OtherPropertyDisplayName { get; set; }
    
        protected override ValidationResult IsValid(object value, ValidationContext validationContext)
        {
            return IsValid(OtherPropertyName, value, validationContext);
        }
    
        private ValidationResult IsValid(string otherProperty, object value, ValidationContext validationContext)
        {
            PropertyInfo otherPropertyInfo = validationContext.ObjectType.GetProperty(otherProperty);
    
            if (otherPropertyInfo == null)
            {
                throw new Exception("Could not find property: " + otherProperty);
            }
    
            var displayAttribute = otherPropertyInfo.GetCustomAttributes(typeof(DisplayAttribute), true).FirstOrDefault() as DisplayAttribute;
    
            if (displayAttribute != null && OtherPropertyDisplayName == null)
            {
                OtherPropertyDisplayName = displayAttribute.GetName();
            }
    
            object otherPropertyValue = otherPropertyInfo.GetValue(validationContext.ObjectInstance, null);
    
            var smaller = (IComparable) value;
            var bigger = (IComparable) otherPropertyValue;
    
            if (smaller == null || bigger == null)
            {
                return null;
            }
    
            if (smaller.CompareTo(bigger) > 0)
            {
                return new ValidationResult(string.Format(ValidatorResource.SmallerThan, validationContext.DisplayName, OtherPropertyDisplayName));
            }
    
            return null;
        }
    }
    

    There is one gotcha. Error message format is defined in resource class property (ValidatorResource.SmallerThan), so it's not pluggable -- I didn't need this. However, I think it still could be a good starting point for you.