Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EditorFor on nullable DateTime - "Nullable object must have a value."

I am attempting to display a form that allows a user to input a new assignment for a person. I'm using a DateTime.cshtml EditorTemplate to handle DateTime values for the assignment. The non-nullable DateTime works fine. The nullable DateTime causes an "InvalidOperationException: Nullable object must have a value."

I have a simple viewmodel that looks like this:

AssignmentViewModel.cs:

public Person Person { get; set; }
public Assignment NewAssignment { get; set; }

Assignment.cs contains:

public DateTime AssignmentStartDate { get; set; }
public DateTime? AssignmentEndDate { get; set; }

My AssignmentController Create() method looks like:

public ViewResult Create(int personId)
{
    Person person = personRepository.GetPersonById(personId);
    var newAssignment = new AssignmentViewModel { Person = person, NewAssignment = new Assignment() };
    return View(newAssignment);
}

My Create.cshtml view looks like this:

@model AssignmentViewModel

@using (Html.BeginForm("Create", "Assignment"))
{
    @Html.Hidden("NewAssignment.PersonId", Model.Person.PersonId)
    @Html.LabelFor(x => x.NewAssignment.AssignmentStartDate):
    @Html.EditorFor(x => x.NewAssignment.AssignmentStartDate.Date, new { cssClass = "datePicker" })
    <br />
    @Html.LabelFor(x => x.NewAssignment.AssignmentEndDate):
    @Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.Value.Date, new { cssClass = "datePicker" })
    <br />
    <input type="submit" value="Send />
}

My DateTime.cshtml EditorTemplate looks like:

@model DateTime?

@{
    String modelValue = "";
    if (Model.HasValue)
    {
        if (Model.Value != DateTime.MinValue)
        {
            modelValue = Model.Value.ToShortDateString();
        }
    }
}

@Html.TextBox("", modelValue, new { @class = "datePicker" })

When I attempt to load the Create view, I get the exception mentioned above on the line "@Html.EditorFor(x => x.NewAssignment.AssignmentEndDate.Value)".

You may be wondering why I'm passing in AssignmentEndDate.Value.Date instead of just passing in AssignmentEndDate; the reason is because I'm trying to get to the point where I'm splitting DateTime into Date and a TimeOfDay field and recombine them with a DateTimeModelBinder. I am using a similar technique to the one shown here and here.

I -can- bypass the error, by changing my controller Create() method to instantiate the ViewModel with AssignmentEndDate set to DateTime.MinValue, but this seems completely wrong for a nullable DateTime:

var newAssignment = new AssignmentViewModel 
                        { 
                            Person = person, 
                            NewAssignment = new Assignment { AssignmentEndDate = DateTime.MinValue } 
                        };

Something strange happens after I "bypass" the error by supplying a value for the nullable DateTime; the un-required nullable DateTime property (AssignmentEndDate.Date) fails client side validation. Trying to submit the form highlights the field in red.

How can I handle this correctly?

like image 904
Nick Silberstein Avatar asked Dec 06 '25 03:12

Nick Silberstein


1 Answers

The problem is that you're trying to retrieve the AssignmentEndDate.Value.Date, but AssignmentEndDate is null, which results in this error.

Since your editor template accepts a DateTime?, you should just pass along the AssignmentEndDate. In other words, remove the .Value.Date from the view:

@Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, new { cssClass = "datePicker" })

Since your editor template is using ToShortDateString(), there's no need to "truncate" the time from the date at all.

Update

Regarding your desire to have separate "Date" and "Time" editors:

You can do this 2 ways.

1 - Your current DateTime? editor renders a field for the Model.Value.Date, so you could simply extend this to also render a field for the Model.Value.TimeOfDay. Example:

@{
  DateTime? modelDate = (Model == null) ? (DateTime?)null : Model.Value.Date;
  TimeSpan? modelTime = (Model == null) ? (TimeSpan?)null : Model.Value.TimeOfDay;
}
@Html.TextBox(..., modelDate, new{@class="datePicker"})
@Html.TextBox(..., modelTime, new{@class="timePicker"})

2 - You could split the above functionality into 2 separate editors, "DateOnly" and "TimeOnly". Then, update your view to call both editors:

@Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, "DateOnly")
@Html.EditorFor(x => x.NewAssignment.AssignmentEndDate, "TimeOnly")

The choice is up to you, and whether you want to keep the Date and Time parts separate or together, but this is how I'd go about solving this problem.

like image 175
Scott Rippey Avatar answered Dec 09 '25 19:12

Scott Rippey



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!