C# – ASP.NET MVC Problem with Html.ListBoxFor()

.netasp.netasp.net-mvcc++

I'm having problems with the helper Html.ListBoxFor().

Here is my ViewModel:

public class NewReservation
{
    public SelectList AvailableServiceDates { get; set; }
    public DateTime SelectedServiceDate { get; set; }
}

Here is my action method:

    public virtual ActionResult New()
    {
        NewReservation newReservation = new NewReservation();
        newReservation.AvailableServiceDates = new SelectList(nhSession.Linq<Service>().Select(x => x.DateTime).Distinct());
        return View(newReservation);
    }

And in my view:

<%:Html.ListBoxFor(x => x.SelectedServiceDate, Model.AvailableServiceDates) %>

Here is the exception:

Value cannot be null. Parameter name:
source

Stack Trace:

[ArgumentNullException: Value cannot
be null. Parameter name: source]
System.Linq.Enumerable.Cast(IEnumerable
source) +4177011
System.Web.Mvc.Html.SelectExtensions.SelectInternal(HtmlHelper
htmlHelper, String optionLabel, String
name, IEnumerable1 selectList,
Boolean allowMultiple, IDictionary
2
htmlAttributes) +723
System.Web.Mvc.Html.SelectExtensions.ListBoxHelper(HtmlHelper
htmlHelper, String name, IEnumerable1
selectList, IDictionary
2
htmlAttributes) +47
System.Web.Mvc.Html.SelectExtensions.ListBoxFor(HtmlHelper1
htmlHelper, Expression
1 expression,
IEnumerable1 selectList,
IDictionary
2 htmlAttributes) +113
System.Web.Mvc.Html.SelectExtensions.ListBoxFor(HtmlHelper1
htmlHelper, Expression
1 expression,
IEnumerable`1 selectList) +85
ASP.views_reservation_new_aspx.__RenderMainContent(HtmlTextWriter
__w, Control parameterContainer) in c:\code\GTCC Culinary
Reservation\src\CulinaryReservation.WebMVC\Views\Reservation\New.aspx:11
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter
writer, ICollection children) +109
System.Web.UI.Control.RenderChildren(HtmlTextWriter
writer) +8
System.Web.UI.Control.Render(HtmlTextWriter
writer) +10
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter
writer, ControlAdapter adapter) +27
System.Web.UI.Control.RenderControl(HtmlTextWriter
writer, ControlAdapter adapter) +100
System.Web.UI.Control.RenderControl(HtmlTextWriter
writer) +25
ASP.views_shared_site_master.__Render__control1(HtmlTextWriter
__w, Control parameterContainer) in c:\code\GTCC Culinary
Reservation\src\CulinaryReservation.WebMVC\Views\Shared\Site.Master:66
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter
writer, ICollection children) +109
System.Web.UI.Control.RenderChildren(HtmlTextWriter
writer) +8
System.Web.UI.Control.Render(HtmlTextWriter
writer) +10
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter
writer, ControlAdapter adapter) +27
System.Web.UI.Control.RenderControl(HtmlTextWriter
writer, ControlAdapter adapter) +100
System.Web.UI.Control.RenderControl(HtmlTextWriter
writer) +25
System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter
writer, ICollection children) +208
System.Web.UI.Control.RenderChildren(HtmlTextWriter
writer) +8
System.Web.UI.Page.Render(HtmlTextWriter
writer) +29
System.Web.Mvc.ViewPage.Render(HtmlTextWriter
writer) +56
System.Web.UI.Control.RenderControlInternal(HtmlTextWriter
writer, ControlAdapter adapter) +27
System.Web.UI.Control.RenderControl(HtmlTextWriter
writer, ControlAdapter adapter) +100
System.Web.UI.Control.RenderControl(HtmlTextWriter
writer) +25
System.Web.UI.Page.ProcessRequestMain(Boolean
includeStagesBeforeAsyncPoint, Boolean
includeStagesAfterAsyncPoint) +3060

This post describes the same problem. And like that post, if I change my NewReservation.SelectedServiceDate from DateTime to string, things seem to work. But everything I have read indicates this is a bug that should have been fixed in an MVC2 release candidate.

Am I doing something wrong? I don't want the property's type to be string, it needs to be DateTime. Shouldn't I be able to use objects that reflect my problem?

Best Solution

A multiple select list could be empty (for example if the user makes no selection) meaning that you cannot bind to a value type (like DateTime). You could use a nullable DateTime instead:

public class NewReservation
{
    public SelectList AvailableServiceDates { get; set; }
    public DateTime? SelectedServiceDate { get; set; }
}

Also notice that in a multiple select list the user could perform multiple selections meaning that binding to a single date doesn't look very natural. IMHO it would be more natural to have this:

public class NewReservation
{
    public SelectList AvailableServiceDates { get; set; }
    public IEnumerable<DateTime> SelectedServiceDates { get; set; }
}