santiagoIT
santiagoIT

Reputation: 9431

Data validation with custom attributes (AttributeTargets.Class) on EF buddy classes

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!

Upvotes: 3

Views: 3578

Answers (1)

santiagoIT
santiagoIT

Reputation: 9431

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;
        }
    }
}

Upvotes: 6

Related Questions