1

I am using foolproof in a MVC project. I am having trouble where it seems like it is doing a textual comparison to figure out whether the end datetime is greater than the start datetime.

Here is the model:

public class TestModel
{
    [Required]
    [DataType(DataType.DateTime)]
    public DateTime start { get; set; }

    [Required]
    [DataType(DataType.DateTime)]
    [GreaterThan("start")]
    public DateTime end { get; set; }
}

the view

@model WebApplication1.Models.TestModel

@{
    ViewBag.Title = "Home Page";
}


@using (Html.BeginForm("Index2", "Home", FormMethod.Post, new { @class = "form-horizontal", role = "form" }))
{
    @Html.AntiForgeryToken()

    @Html.ValidationSummary("", new { @class = "text-danger" })
    <div class="form-group">
        @Html.LabelFor(m => m.start, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.start, "{0:dd/MM/yyyy HH:mm}", new { @class = "form-control datetimepicker" })
        </div>
    </div>
    <div class="form-group">
        @Html.LabelFor(m => m.end, new { @class = "col-md-2 control-label" })
        <div class="col-md-10">
            @Html.TextBoxFor(m => m.end, "{0:dd/MM/yyyy HH:mm}", new { @class = "form-control datetimepicker" })
        </div>
    </div>
    <div class="form-group">
        <div class="col-md-offset-2 col-md-10">
            <input type="submit" value="Set password" class="btn btn-default" />
        </div>
    </div>
}

@section Scripts {
    @Scripts.Render("~/bundles/jqueryval")
}

The controller

    public ActionResult Index()
    {
        TestModel model = new TestModel();
        // model.start = new DateTime(2016, 5, 31);
        //model.end = new DateTime(2016, 8, 31);
        model.start = DateTime.Now;
        model.end = DateTime.Now.AddMinutes(30);
        return View(model);
    }

You can download my project from to try out: https://www.dropbox.com/s/pf0lkg297hq0974/WebApplication1.zip?dl=0

There are two issues: 1) When you make start date 11/07/2016 23:51 and end date 02/08/2016 00:21 you get a validation error as it thinks the end date is less than the start date. It seems like a textual comparison to me.

2) Also if you un-comment out the two model.start and model.end date initialization statements you get invalid date when submitting.

Note I was using bootstrap datetimepicker but commented out the bit where it initialises it when the document is ready. I thought it had something to do with the issue but seems not. Eventually I want to have datetimepicker working as well.

Also note I am in Australia so the date format is dd/mm/yyyy

rukiman
  • 597
  • 10
  • 32
  • I'm guessing foolproof is buggy here. Any other way of doing annotation validation ? – rukiman Aug 01 '16 at 23:02
  • The issue is the `jquery.validate.js` (used by foolproof) validates dates based on `MM/dd/yyyy` format (so `end=02/08/2016` is 8th Feb and `start=11/07/2016` is 7th Dec which means `end` is less than `start`). Refer [this answer](http://stackoverflow.com/questions/39677035/date-of-birth-validation-keeps-showing/39682410#39682410) for some options. –  Oct 04 '16 at 02:41
  • Also [this answer](http://stackoverflow.com/questions/27285458/jquery-ui-datepicker-and-mvc-view-model-type-datetime/27286969#27286969) if you using the jquery-ui datepicker (I assume bootstrap datetimepicker will also have a similar method for parsing the value to a specific date format) –  Oct 04 '16 at 02:41
  • Note also in relation to [this chat](http://chat.stackoverflow.com/rooms/118397/discussion-between-stephen-muecke-and-rukiman) - you did not ping me (by starting the message with `@username` so I did not realize you had added a comment until now) –  Oct 04 '16 at 02:43

1 Answers1

1

in your HTML ur posting to Index2 change that to Index Convert String Back to DateTime in GreaterThanAttribute in your application and then Compare

try this I have tested this Implementation

The Controller

 public class TestController : Controller
{
    // GET: Test

    [HttpGet]
    public ActionResult Index()
    {

        var model = new TestModel();
        // model.start = new DateTime(2016, 5, 31);
        //model.end = new DateTime(2016, 8, 31);
        model.start = DateTime.Now;
        model.end = DateTime.Now.AddMinutes(30);
        return View(model);


    }
    [HttpPost]
    public ActionResult Index(TestModel model)
    {
        if (ModelState.IsValid)
        {

            // model.start = new DateTime(2016, 5, 31);
            //model.end = new DateTime(2016, 8, 31);
            model.start = DateTime.Now;
            model.end = DateTime.Now.AddMinutes(30);
             return View("Index", model);
        }
        return View("Index", model);

    }
}

Your TestModel

public class TestModel
{
    [Required]
    [DataType(DataType.DateTime)]
    public DateTime start { get; set; }

    [Required]
    [DataType(DataType.DateTime)]
    [GreaterThan("start", "Your Error Message")]
    public DateTime end { get; set; }
}

your GreaterThenAttribute.cs

 public class GreaterThanAttribute : ValidationAttribute, IClientValidatable
{

    public string otherPropertyName;
    public GreaterThanAttribute()
    {
    }
    public GreaterThanAttribute(string otherPropertyName, string errorMessage) : base(errorMessage)
    {
        this.otherPropertyName = otherPropertyName;
    }

    protected override ValidationResult IsValid
        (object value, ValidationContext validationContext)
    {
        ValidationResult validationResult = ValidationResult.Success;
        try
        {
            // Using reflection we can get a reference to the other date property, in this example the project start date
            var containerType = validationContext.ObjectInstance.GetType();
            var field = containerType.GetProperty(this.otherPropertyName);
            var extensionValue = field.GetValue(validationContext.ObjectInstance, null);
            var datatype = extensionValue.GetType();

            //var otherPropertyInfo = validationContext.ObjectInstance.GetType().GetProperty(this.otherPropertyName);
            if (field == null)
                return new ValidationResult(String.Format("Unknown property: {0}.", otherPropertyName));
            // Let's check that otherProperty is of type DateTime as we expect it to be
            if ((field.PropertyType == typeof(DateTime) ||
                 (field.PropertyType.IsGenericType && field.PropertyType == typeof(Nullable<DateTime>))))
            {
                DateTime toValidate = (DateTime)value;
                DateTime referenceProperty = (DateTime)field.GetValue(validationContext.ObjectInstance, null);
                // if the end date is lower than the start date, than the validationResult will be set to false and return
                // a properly formatted error message
                if (toValidate.CompareTo(referenceProperty) < 1)
                {
                    validationResult = new ValidationResult(ErrorMessageString);
                }
            }
            else
            {
                validationResult = new ValidationResult("An error occurred while validating the property. OtherProperty is not of type DateTime");
            }
        }
        catch (Exception ex)
        {
            // Do stuff, i.e. log the exception
            // Let it go through the upper levels, something bad happened
            throw ex;
        }

        return validationResult;
    }

    public IEnumerable<ModelClientValidationRule> GetClientValidationRules
        (ModelMetadata metadata, ControllerContext context)
    {
        var rule = new ModelClientValidationRule
        {
            ErrorMessage = FormatErrorMessage(metadata.GetDisplayName()),
            ValidationType = "isgreater",
        };
        rule.ValidationParameters.Add("otherproperty", otherPropertyName);
        yield return rule;
    }
}
gurdeep
  • 44
  • 3