I have a ViewModel with a StartDate and an EndDate. Obviously, I need to validate StartDate <= EndDate.
I created the StartDateBeforeEndDate
validation attribute and decorated the ViewModel class with it:
public class ValidProgramDisplayStartDateAttribute : ValidationAttribute
{
public override bool IsValid(object value)
{
ProgramCreateOrEditViewModel vm = value as ProgramCreateOrEditViewModel;
if (vm == null) return true; //not our problem
if (!vm.EndDisplay.HasValue) return true;
//if you don't set an end date, you can't be invalid
return vm.StartDisplay <= vm.EndDisplay;
}
}
...
[ValidProgramDisplayStartDate(ErrorMessage="The program start display date cannot be after the program display end date.")]
public class ProgramCreateOrEditViewModel
{
[Required(ErrorMessage = "A program title is required.")]
[StringLength(255, ErrorMessage = "The program title cannot exceed 255 characters.")]
public virtual string Title { get; set; }
private DateTime _startDisplayDate = DateTime.Now;
[Display(Name = "Display Start Date")]
[Required(ErrorMessage = "A start display date is required")]
[DataType(DataType.Date)]
public virtual DateTime StartDisplay { get { return _startDisplayDate; } set { _startDisplayDate = value; } }
[Display(Name="Display End Date")]
[DataType(DataType.Date)]
public virtual DateTime? EndDisplay { get; set; }
...more properties omitted...
}
The Title is correctly validated and its validation message appears in the validation summary as expected, but a breakpoint on IsValid never fires, and no validation error appears if you give an invalid pairing.
The ValidationAttribute
works correctly in a unit test that creates a ViewModel object, populates the dates, creates the ValidationAttribute
object and calls its IsValid
method. It also works correctly if I set a breakpoint on the POST action in the Controller, and use Visual Studio's immediate window to construct the ValidationAttribute
and pass it the received ViewModel.
I don't really care about client side validation at this stage; that's filed under "nice to have but who's got the time"? The server-side validation however, is essential.
View:
@model MyProject.Web.Mvc.Controllers.ViewModels.ProgramCreateOrEditViewModel
@using MvcContrib.FluentHtml
@{
ViewBag.Title = "Index";
}
<h2>Index</h2>
@using (Html.BeginForm())
{
<fieldset class="program"><legend>Program</legend>
@if (!ViewData.ModelState.IsValid)
{
<p class="error">Please correct all errors listed below. The program cannot be saved while errors remain.</p>
@Html.ValidationSummary(false)
}
@Html.HiddenFor(m => m.Id)
<h3>
@Html.LabelFor(m => m.Title)
@Html.TextBoxFor(m => m.Title)</h3>
<div id="accordion">
<h3><a href="#">Dates:</a></h3>
<div class="section">
Dates related to <em>displaying</em> the program on our site:<br />
@Html.LabelFor(m => m.StartDisplay)
@Html.EditorFor(m => m.StartDisplay, new { _class = "date" })
@Html.LabelFor(m => m.EndDisplay)
@Html.EditorFor(m => m.EndDisplay, new { _class = "date enddisplay" })
</div>
... more form fields omitted ...
</div>
<p>
<input type="submit" value="Save" /></p>
</fieldset>
}
And finally, the editor template:
@model DateTime?
@Html.TextBox("", Model.HasValue ? Model.Value.ToString("MM/dd/yyyy") : String.Empty, new { @class = "date" })