Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

mvc4 data annotation compare two dates

I have these two fields in my model:

[Required(ErrorMessage="The start date is required")]
[Display(Name="Start Date")]
[DisplayFormat(DataFormatString = "{0,d}")]
public DateTime startDate { get; set; }

[Required(ErrorMessage="The end date is required")]
[Display(Name="End Date")]
[DisplayFormat(DataFormatString = "{0,d}")]
public DateTime endDate{ get; set; }

I require that endDate must be greater than startDate. I tried using [Compare("startDate")] but this only works for the equal operation.

What should I use for the "greater than" operation?

like image 971
user2208349 Avatar asked Nov 09 '13 19:11

user2208349


3 Answers

Take a look at Fluent Validation or MVC Foolproof Validation: those can help you a lot.

With Foolproof for example there is a [GreaterThan("StartDate")] annotation than you can use on your date property.

Or if you don't want to use other libraries, you can implement your own custom validation by implementing IValidatableObject on your model:

public class ViewModel: IValidatableObject
{
    [Required]
    public DateTime StartDate { get; set; }
    [Required]    
    public DateTime EndDate { get; set; } 

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
       if (EndDate < StartDate)
       {
           yield return new ValidationResult(
               errorMessage: "EndDate must be greater than StartDate",
               memberNames: new[] { "EndDate" }
          );
       }
    }
}
like image 114
Davor Zlotrg Avatar answered Nov 02 '22 23:11

Davor Zlotrg


IValidatableObject interface provides a way for an object to be validated which implements IValidatableObject.Validate(ValidationContext validationContext) method. This method always return IEnumerable object. That's why you should create list of ValidationResult objects and errors are added to this and return. Empty list means to validate your conditions. That is as follows in mvc 4...

 public class LibProject : IValidatableObject
{
    [Required(ErrorMessage="Project name required")]

    public string Project_name { get; set; }
    [Required(ErrorMessage = "Job no required")]
    public string Job_no { get; set; }
    public string Client { get; set; }
    [DataType(DataType.Date,ErrorMessage="Invalid Date")]
    public DateTime ExpireDate { get; set; }


    IEnumerable<ValidationResult> IValidatableObject.Validate(ValidationContext validationContext)
    {
        List < ValidationResult >  res =new List<ValidationResult>();
        if (ExpireDate < DateTime.Today)
        {
            ValidationResult mss=new ValidationResult("Expire date must be greater than or equal to Today");
            res.Add(mss);

        }
        return res; 
    }
}
like image 11
Mahbub Avatar answered Nov 02 '22 23:11

Mahbub


Borrowing heavily from the responses from Alexander Gore and Jaime Marín in a related StackOverflow question1, I created five classes that enable comparing two fields in the same model using GT, GE, EQ, LE, and LT operators, provided they implement IComparable. So it can be used for pairs of dates, times, integers, and strings, for example.

It would be nice to merge these all into one class and take the operator as an argument, but I don't know how. I left the three exceptions as is because, if thrown, they really represent a form design problem, not a user input problem.

You just use it in your model like this, and the file with the five classes follows:

    [Required(ErrorMessage = "Start date is required.")]
    public DateTime CalendarStartDate { get; set; }

    [Required(ErrorMessage = "End date is required.")]
    [AttributeGreaterThanOrEqual("CalendarStartDate", 
        ErrorMessage = "The Calendar end date must be on or after the Calendar start date.")]
    public DateTime CalendarEndDate { get; set; }


using System;
using System.ComponentModel.DataAnnotations;

//Contains GT, GE, EQ, LE, and LT validations for types that implement IComparable interface.
//https://stackoverflow.com/questions/41900485/custom-validation-attributes-comparing-two-properties-in-the-same-model

namespace DateComparisons.Validations
{
    [AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
    public class AttributeGreaterThan : ValidationAttribute
    {
        private readonly string _comparisonProperty;
        public AttributeGreaterThan(string comparisonProperty){_comparisonProperty = comparisonProperty;}

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if(value==null) return new ValidationResult("Invalid entry");

        ErrorMessage = ErrorMessageString;

        if (value.GetType() == typeof(IComparable)) throw new ArgumentException("value has not implemented IComparable interface");
        var currentValue = (IComparable)value;

        var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
        if (property == null) throw new ArgumentException("Comparison property with this name not found");

        var comparisonValue = property.GetValue(validationContext.ObjectInstance);
        if(!ReferenceEquals(value.GetType(), comparisonValue.GetType()))
            throw new ArgumentException("The types of the fields to compare are not the same.");

        return currentValue.CompareTo((IComparable)comparisonValue) > 0 ? ValidationResult.Success : new ValidationResult(ErrorMessage);
    }
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class AttributeGreaterThanOrEqual : ValidationAttribute
{
    private readonly string _comparisonProperty;
    public AttributeGreaterThanOrEqual(string comparisonProperty) { _comparisonProperty = comparisonProperty; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null) return new ValidationResult("Invalid entry");
        ErrorMessage = ErrorMessageString;

        if (value.GetType() == typeof(IComparable)) throw new ArgumentException("value has not implemented IComparable interface");
        var currentValue = (IComparable)value;

        var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
        if (property == null) throw new ArgumentException("Comparison property with this name not found");

        var comparisonValue = property.GetValue(validationContext.ObjectInstance);
        if (!ReferenceEquals(value.GetType(), comparisonValue.GetType()))
            throw new ArgumentException("The types of the fields to compare are not the same.");

        return currentValue.CompareTo((IComparable)comparisonValue) >= 0 ? ValidationResult.Success : new ValidationResult(ErrorMessage);

    }
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class AttributeEqual : ValidationAttribute
{
    private readonly string _comparisonProperty;
    public AttributeEqual(string comparisonProperty) { _comparisonProperty = comparisonProperty; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null) return new ValidationResult("Invalid entry");
        ErrorMessage = ErrorMessageString;

        if (value.GetType() == typeof(IComparable)) throw new ArgumentException("value has not implemented IComparable interface");
        var currentValue = (IComparable)value;

        var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
        if (property == null) throw new ArgumentException("Comparison property with this name not found");

        var comparisonValue = property.GetValue(validationContext.ObjectInstance);
        if (!ReferenceEquals(value.GetType(), comparisonValue.GetType()))
            throw new ArgumentException("The types of the fields to compare are not the same.");

        return currentValue.CompareTo((IComparable)comparisonValue) == 0 ? ValidationResult.Success : new ValidationResult(ErrorMessage);
    }
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class AttributeLessThanOrEqual : ValidationAttribute
{
    private readonly string _comparisonProperty;
    public AttributeLessThanOrEqual(string comparisonProperty) { _comparisonProperty = comparisonProperty; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null) return new ValidationResult("Invalid entry");
        ErrorMessage = ErrorMessageString;

        if (value.GetType() == typeof(IComparable)) throw new ArgumentException("value has not implemented IComparable interface");
        var currentValue = (IComparable)value;

        var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
        if (property == null) throw new ArgumentException("Comparison property with this name not found");

        var comparisonValue = property.GetValue(validationContext.ObjectInstance);
        if (!ReferenceEquals(value.GetType(), comparisonValue.GetType()))
            throw new ArgumentException("The types of the fields to compare are not the same.");

        return currentValue.CompareTo((IComparable)comparisonValue) <= 0 ? ValidationResult.Success : new ValidationResult(ErrorMessage);
    }
}

[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Parameter)]
public class AttributeLessThan : ValidationAttribute
{
    private readonly string _comparisonProperty;
    public AttributeLessThan(string comparisonProperty) { _comparisonProperty = comparisonProperty; }

    protected override ValidationResult IsValid(object value, ValidationContext validationContext)
    {
        if (value == null) return new ValidationResult("Invalid entry");
        ErrorMessage = ErrorMessageString;

        if (value.GetType() == typeof(IComparable)) throw new ArgumentException("value has not implemented IComparable interface");
        var currentValue = (IComparable)value;

        var property = validationContext.ObjectType.GetProperty(_comparisonProperty);
        if (property == null) throw new ArgumentException("Comparison property with this name not found");

        var comparisonValue = property.GetValue(validationContext.ObjectInstance);
        if (!ReferenceEquals(value.GetType(), comparisonValue.GetType()))
            throw new ArgumentException("The types of the fields to compare are not the same.");

        return currentValue.CompareTo((IComparable)comparisonValue) < 0 ? ValidationResult.Success : new ValidationResult(ErrorMessage);
    }
}

}


like image 3
Jeffrey Roughgarden Avatar answered Nov 02 '22 21:11

Jeffrey Roughgarden