3

I have an Entity Framework generated class with the following properties:

public DateTime LaunchDate;
public DateTime ExpirationDate;

I need to enforce that ExpirationDate > LaunchDate.

I am using a buddy class as described in various posts. I am applying custom validation attributes (on properties) to other properties in the same buddy class and these are working.

Since I need to compare two properties I am using an attribute directly on the class (AttributeTargets.Class)

Here is my custom validation attribute:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class PropertyMustBeGreaterThanAttribute : ValidationAttribute
{
    private const string _defaultErrorMessage = "'{0}' must be greater than '{1}'!";

    public PropertyMustBeGreaterThanAttribute(string property, string greaterThan)
        : base(_defaultErrorMessage)
    {
        GreaterThan = greaterThan;
        Property = property;
    }

    public string Property { get; private set; }
    public string GreaterThan { get; private set; }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
            GreaterThan, Property);
    }

    public override bool IsValid(object value)
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
        IComparable greaterThan = properties.Find(GreaterThan, true /* ignoreCase */).GetValue(value) as IComparable;
        IComparable property = properties.Find(Property, true /* ignoreCase */).GetValue(value) as IComparable;
        return greaterThan.CompareTo(property) > 0;
    }
}

First I am unsure to which class I need to apply the attribute to:

[MetadataType(typeof(PromotionValidation))]
[PropertyMustBeGreaterThanAttribute("RetailPrice")] // apply it here
public partial class Promotion
{
    [PropertyMustBeGreaterThanAttribute("ExpirationDate", "LaunchDate")] // or here ???
    public class PromotionValidation
    {

Second, it's not working and I have no clue why!!! I've tried adding attribute to both classes. Breakpoints in the constructor are hit (PropertyMustBeGreaterThanAttribute ), but IsValid is never called.

Pulling my hair out already....

Thanks!

santiagoIT
  • 9,411
  • 6
  • 46
  • 57

1 Answers1

6

Got it to work by implementing TypeID. I assigned the attribute to the partial class:

    [MetadataType(typeof(PromotionValidation))]
    [PropertyMustBeGreaterThanAttribute("RetailPrice", "DiscountedPrice")]
    [PropertyMustBeGreaterThanAttribute("ExpirationDate", "LaunchDate")]
    [PropertyMustBeGreaterThanAttribute("ClaimDeadline", "ExpirationDate")]
    public partial class Promotion
    {
... 

Here is the listing in case anyone needs it:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = true, Inherited = true)]
public class PropertyMustBeGreaterThanAttribute : ValidationAttribute
{
    private const string _defaultErrorMessage = "'{1}' must be greater than '{0}'!";
    private readonly object _typeId = new object();

    public PropertyMustBeGreaterThanAttribute(string property, string greaterThan)
        : base(_defaultErrorMessage)
    {
        GreaterThan = greaterThan;
        Property = property;
    }

    public string Property { get; private set; }
    public string GreaterThan { get; private set; }

    public override string FormatErrorMessage(string name)
    {
        return String.Format(CultureInfo.CurrentUICulture, ErrorMessageString,
            GreaterThan, Property);
    }

    public override bool IsValid(object value)
    {
        PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(value);
        IComparable greaterThan = properties.Find(GreaterThan, true /* ignoreCase */).GetValue(value) as IComparable;
        IComparable property = properties.Find(Property, true /* ignoreCase */).GetValue(value) as IComparable;
        return greaterThan.CompareTo(property) > 0;
    }

    public override object TypeId
    {
        get
        {
            return _typeId;
        }
    }
}
santiagoIT
  • 9,411
  • 6
  • 46
  • 57
  • I tried the code but when I try to call Validator.TryValidateObject, It's not stepping in to the "bool IsValid(object value)" method. How will I be able to achieve that? – Lance Feb 05 '14 at 05:34
  • @LawrenceA.Contreras it has been a long time since I stopped using asp.net mvc. I use node.js now, but anyhow I do remember this issue and I do remember it was working as expected. I checked the code posted above and it is complete. I did never invoke validation directly, as validation was done as part of the Data validation framework. Did you look at http://stackoverflow.com/questions/4426854/how-to-call-validationattributes-manually-dataannotations-and-modelstate ? – santiagoIT Feb 06 '14 at 16:37