Question

I'm having trouble getting my head around how to extend the ValidationAttribute class when I want to compare across entities.

Example, I have the following 2 entities:

public class Incident
{
    public DateTime IncidentDate {get;set;}
    //other properties

    public virtual ICollection<Grievance> Grievances {get;set;}
}

public class Grievance
{
    public DateTime FileDate {get;set;}
    public DateTime? HearingDate {get;set;}
    //other properties

    public virtual Incident Incident {get;set;}  
}

I want to validate (server side, because this DLL is going to be used by multiple different projects) that the two DateTime of a Grievance properties are after the IncidentDate property for the corresponding Incident.

Ideally I'd want to write a HearingDateAttribute : ValidationAttribute type of class, but I'm not sure how to get the IncidentDate property to do a compare to for the IsValid override.

Edit

Ok this seems to work, but I'm not sure that it's best practice:

public sealed class HearingDateAttribute : ValidationAttribute
{
    protected override ValidationResult IsValid(object value, ValidationContext context)
    {
        var hearingDate = (DateTime)value;
        var grievance = (Grievance)context.ObjectInstance;
        var incidentDate = grievance.Incident.IncidentDate;

        if (hearingDate > incidentDate.Value)
        {
            return ValidationResult.Success;
        }

        return null;
    }
}
Was it helpful?

Solution

Data Annotations are good for triggering simple validation on individual properties but aren't good for model level validation. You should implement IValidatableObject on you entity. Something like this:

public class Grievance: IValidatebleObject
{

public IEnumerable<ValidationResult> Validate(ValidationContext validationContext) 
     { 
         if (FileDate < Incident.IncidentDate 
            ||(HearingDate.HasValue && HearingDate.Value < Incident.IncidentDate)) 
         { 
             yield return new ValidationResult 
              ("Dates invalid", 
               new[] { "FileDate", “HearingDate”, "Incident.IncidentDate" }); 
         } 
     }  
}

Reference:

http://msdn.microsoft.com/en-us/data/gg193959.aspx

OTHER TIPS

You could write something like that for your Grievance class

public bool AreDatesBiggerThanIncident()
{
    return FileDate>Incident.IncidentDate && (HearingDate.HasValue? HearingDate.Value>Incident.IncidentDate : true);
}

I assumed you don't want your nullable datetime to break the check, but you could expand it, f.e:

public bool AreDatesBiggerThanIncident(bool returnFalseIfNullableEmpty=false)
{
    return FileDate>Incident.IncidentDate && (HearingDate.HasValue? HearingDate.Value>Incident.IncidentDate : !returnFalseIfNullableEmpty);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top