Question

Is there any default validation for MVC 5 where I can set min and max value of date?

In my model i want date validation

    public class MyClass
    {               
        [Required(ErrorMessage="Start date and time cannot be empty")]
        //validate:Must be greater than current date
        [DataType(DataType.DateTime)]
        public DateTime StartDateTime { get; set; }

        [Required(ErrorMessage="End date and time cannot be empty")]
        //validate:must be greater than StartDate
        [DataType(DataType.DateTime)]
        public DateTime EndDateTime { get; set; }            
    }

Ps: According to this Asp.Net Website, there is a problem in using the Range validator for DateTime, and it is not recommended.

Note: jQuery validation does not work with the Range attribute and DateTime. For example, the following code will always display a client side validation error, even when the date is in the specified range:

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]

You will need to disable jQuery date validation to use the Range attribute with DateTime. It's generally not a good practice to compile hard dates in your models, so using the Range attribute and DateTime is discouraged.

I also, know that there are Nuget packages like Fluent Validation and Foolproof that does the trick for validating and checking if one date is greater than other, but I wanted to know if by default there is something to check the date's min and max value.

From what I saw in Whats new in MVC 5.1, There is support for MaxLength and MinLength validation.

No correct solution

OTHER TIPS

There is no need to disable jQuery date validation (and that is likely to cause other issues). You just need to override the range method of the $.validator.

By default, it works with numeric values (and then falls back to a string comparison), so you can add the following script (after jquery.validate.js and jquery.validate.unobtrusive.js, but not wrapped in $(document).ready

$.validator.methods.range = function(value, element, param) {
    if ($(element).attr('data-val-date')) {
        var min = $(element).attr('data-val-range-min');
        var max = $(element).attr('data-val-range-max');
        var date = new Date(value).getTime();
        var minDate = new Date(min).getTime();
        var maxDate = new Date(max).getTime();
        return this.optional(element) || (date >= minDate && date <= maxDate);
    }
    // use the default method
    return this.optional( element ) || ( value >= param[ 0 ] && value <= param[ 1 ] );
};

Then you can use the RangeAttribute on your property

[Range(typeof(DateTime), "1/1/1966", "1/1/2020")]
public DateTime Date { get; set; }

I'd do this with the IValidatableObject interface from System.ComponentModel.DataAnnotations, which allows you to add extra validation rules where you can do a lot more checking. Add the interface to your class, and then implement the Validate method, where you can compare the StartDateTime against the current date/time, and also compare the EndDateTime with the StartDateTime, e.g.

public class MyClass : IValidatableObject
{               
    [Required(ErrorMessage="Start date and time cannot be empty")]
    //validate:Must be greater than current date
    [DataType(DataType.DateTime)]
    public DateTime StartDateTime { get; set; }

    [Required(ErrorMessage="End date and time cannot be empty")]
    //validate:must be greater than StartDate
    [DataType(DataType.DateTime)]
    public DateTime EndDateTime { get; set; }       

    public IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
    {
        List<ValidationResult> results = new List<ValidationResult>();

        if (StartDateTime < DateTime.Now)
        {
            results.Add(new ValidationResult("Start date and time must be greater than current time", new []{"StartDateTime"}));
        }

        if (EndDateTime <= StartDateTime)
        {
            results.Add(new ValidationResult("EndDateTime must be greater that StartDateTime", new [] {"EndDateTime"}));
        }

        return results;
    }     
}

The only potential drawback to this is that Validate runs server-side, not in jQuery validation.

Normally I use this solution when I want to validate something in the server. I know that you can rely on the Validation Model that MVC uses, but when I do the validations, I am trying to use a separate project in case I need to make unit test for them. Lets say you have two applications, one Desktop and one Web, then both can share the ValidationProject instead of repeating the code in both application for the same "View"/"Screen". The idea here is isolate the Validation Project as an independent project of your solution.

So you can download FluentValidation project from NuGet, FluentValidation uses Rules inside the constructor, you can see the documentation here https://github.com/JeremySkinner/FluentValidation/wiki

Your Date rule can be used ont this way for example, there you can put rules for your min value and your max value also:

public class CustomerValidator: AbstractValidator<Customer> {
  public CustomerValidator() {
                    RuleFor(customer => customer.startDate)
                    .NotNull()
                    .WithMessage("You should select a start date")
                    .Must(d => d.HasValue && d.Value <= DateTime.Today)
                    .WithMessage("The start date should not be greather than current month/year");
  }

Now inside your controller

[HttpPost]
public ActionResult Index(Customer c)
{
  if(ModelState.IsValid)
  {
    var validator= new CustomerValidator();
    var valResult = validator.Validate(c);

    if (valResult.Errors.Count != 0)
    {
       valResult.Errors.ForEach(x => ModelState.AddModelError(x.PropertyName, x.ErrorMessage));
       return View(vm);
    }

    //Your code here
  }
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top