views:

31

answers:

1

I have the following ValidationAttribute class

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public sealed class DateValidationAttribute : ValidationAttribute
{
    public DateValidationAttribute(string leftDateProperty, CompareOperator compareOperator, string rightDateProperty, string errorMessage)
            : base(errorMessage)
    {
        LeftDateProperty = leftDateProperty;
        Operator = compareOperator;
        RightDateProperty = rightDateProperty;
    }
    ...
    ...
}

It takes two date property names and an operator in the constructor.

In the validation method the result of the statement LeftDate Operator RightDate is returned.

public override bool IsValid(object value)
{
    DateTime leftDate;
    DateTime rightDate;

    // Get all properties on the view model
    PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);

    DateTime rightDate = (DateTime)properties.Find(RightDateProperty, true).GetValue(value);
    DateTime leftDate = (DateTime)properties.Find(LeftDateProperty, true).GetValue(value);

    // Perform rule check
    switch (Operator)
    {
        case CompareOperator.Equal:
            return leftDate.Equals(rightDate);
        case CompareOperator.Greater:                    
            return leftDate > rightDate;
        case CompareOperator.Lesser:                    
            return leftDate < rightDate;
        case CompareOperator.GreaterOrEqual:                    
            return leftDate >= rightDate;
        case CompareOperator.LesserOrEqual:                    
            return leftDate <= rightDate;
        default:
            return false;
    }
}

Because this is an AttriuteTargets.Class attribute I know it is impossible for the framework to know which property that is causing the validation to fail. But I know that it is Left Date Property that is failing and therefore I want to set the Id of the error in the modelstate to this property. The reason for this is that I want the failing field to be marked in the form.

Question: How can I modify the error item added to the error collection in ModelState so that its id corresponds to a specific field in the form?

A: 

I found a better way of doing this using IDataErrorInfo

Here is how I'm doing it. It's not that generic as the example in the question. With this solution you have to code each check. But now the validation will work nice all the way up to the javascript and highlighting of the failing input element.

public class TestModel: IDataErrorInfo
{

    public TestModel()
    {
    }

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

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

    #region IDataErrorInfo Members

    public string Error
    {
        get
        {
            return string.Empty;
        }
    }

    public string this[string columnName]
    {
        get
        {
            switch (columnName)
            {
                case "StartDate":
                    {
                        if (StartDate < DateTime.Today)
                        {
                            return "Start date must not be a date in the past";
                        }
                        break;
                    }
                case "EndDate":
                    {
                        if (EndDate < StartDate)
                        {
                            return "End date must not be a date before start date";
                        }
                        break;
                    }   
                default:
                    return string.Empty;
            }
            return string.Empty;
        }
    }
}
Andreas