views:

1037

answers:

3

I have two listboxes, one for available servers and one for selected servers. A user moves items from the first box to the second to indicate their selection.

<table id="ServerLists" style="width: 100%;">
  <tr>
    <td style="width: 40%;">
      <%=Html.ListBox("AvailableServerList", Model.AvailableServerList, New With {.size = 8, .ondblclick = "addServer();"})%>
    </td>
    <td style="width: 20%;">
      <button type="button" onclick="addAll();">Add All &gt;&gt;</button>
      <button type="button" onclick="addServer();">Add &gt;</button>
      <button type="button" onclick="removeServer();">&lt; Remove</button>
      <button type="button" onclick="removeAll();">&lt;&lt; Remove All</button>
    </td>
    <td style="width: 40%;">
      <%=Html.ListBox("SelectedServerList", Model.SelectedServerList, New With {.size = 8, .ondblclick = "removeServer();"})%>
    </td>
  </tr>
</table>

If no servers are added to the second listbox I add an error to the ModelState.

The problem is that if there are no entries in the listbox MVC throws a NullReferenceException when rendering the view with the validation warnings.

[NullReferenceException: Object reference not set to an instance of an object.]
   System.Web.Mvc.HtmlHelper.GetModelStateValue(String key, Type destinationType) +63  
   System.Web.Mvc.Html.SelectExtensions.SelectInternal(HtmlHelper htmlHelper, String optionLabel, String name, IEnumerable`1 selectList, Boolean allowMultiple, IDictionary`2 htmlAttributes) +155  
   System.Web.Mvc.Html.SelectExtensions.ListBox(HtmlHelper htmlHelper, String name, IEnumerable`1 selectList, Object htmlAttributes) +62  
   ASP.views_serveraccess_create_aspx.__RendercMain(HtmlTextWriter __w, Control parameterContainer) in C:\Documents and Settings\nfoster\My Documents\Visual Studio Projects\Client Portal 3\Trunk\src\Stargate3.Web\Views\ServerAccess\Create.aspx:148  
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +256  
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +19  
   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) +99
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +134
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +19
   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) +99
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   ASP.views_shared_site_master.__Render__control1(HtmlTextWriter __w, Control parameterContainer) in C:\Documents and Settings\nfoster\My Documents\Visual Studio Projects\Client Portal 3\Trunk\src\Stargate3.Web\Views\Shared\Site.Master:97
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +256
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +19
   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) +99
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +134
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +19
   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) +99
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   System.Web.UI.Control.RenderChildrenInternal(HtmlTextWriter writer, ICollection children) +134
   System.Web.UI.Control.RenderChildren(HtmlTextWriter writer) +19
   System.Web.UI.Page.Render(HtmlTextWriter writer) +29
   System.Web.Mvc.ViewPage.Render(HtmlTextWriter writer) +57
   System.Web.UI.Control.RenderControlInternal(HtmlTextWriter writer, ControlAdapter adapter) +27
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer, ControlAdapter adapter) +99
   System.Web.UI.Control.RenderControl(HtmlTextWriter writer) +25
   System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) +1266

Has anyone else had this issue? Are there any workarounds I can try? I'm probably going to have to insert a [please select a server] entry in there if no choices have been made, but a fix for this problem would be appreciated.

EDIT: Added controller action as requested

<AcceptVerbs(HttpVerbs.Post)> _
Function Create(ByVal collection As FormCollection) As ActionResult
  Dim acceptedFields() As String = {"StartTime", "EndTime", "ReasonForAccess", "WorkToBeDone", "RegressionPlan", "Servers", "AccessAccount", "Password"}
  Dim accessRequest As New ServerAccessRequest
  UpdateModel(Of ServerAccessRequest)(accessRequest, acceptedFields)
  If accessRequest.IsValid Then
    'TODO: Do database stuff here
    Return View("AccessRequestConfirmation")
  Else
    For Each violation In accessRequest.GetRuleViolations
      ModelState.AddModelError(violation.PropertyName, violation.ErrorMessage)
    Next
  End If
  Return View(New ServerAccessViewModel(accessRequest))
End Function
A: 

Is your Model.SelectedServerList a null value? Model.AvailableServerList is probably a collection of objects, but I don't know if Model.SelectedServerList is not null.

If all else fails, you can do like you say and add a dummy "Please Select a Server" option to the second list.

EDIT: I'd recommend switching to a 'strongly typed method signature' (what I call it anyways) - aka:

[AcceptVerbs(HttpVerbs.Post)]
public ActionResult Create(ServerAccessViewModel data)
{
    // Do stuff

    return View(data);
}

Looking at the control execution, it seems that if the validation succeeds, you are returning a different view to the user, whereas when the validation fails, you are passing back a new instance of the strongly typed view. Are you having to instantiate any components of your view object on the initial view?

EDIT2: I haven't been able to reproduce your exact error message. In the mean time though, I'd say using a foreach loop instead of the ListBox would let you better control the output and prevent the exception.

Tejs
Model.SelectedServerList is an System.Web.Mvc.SelectList with no items in it.
Nick
Can you post the controller action where you do the validation?
Tejs
Added. There's nothing funky in there, just checking for validation errors and passing them into the ModelState.
Nick
Gimme a few minutes, I'm trying to mock up this issue in a temp project.
Tejs
+4  A: 

See the release notes for ASP.NET MVC 2 Preview 1; apparently this (NullReferenceException when passing null into model state) is a know bug in ASP.NET MVC.

Wyatt Barnett
That's a bummer, I'll have to find a different way to highlight the exception.
Nick
+3  A: 

I think this post will help you out (it fixed my problem which was very similar to yours):

http://www.crankingoutcode.com/?aspxerrorpath=/2009/02/01/IssuesWithAddModelErrorSetModelValueWithMVCRC1.aspx

The take away is that for some scenarios (it appears that validation errors for Html.ListBox is one of them) you need to make a call to ModelState.SetModelValue()

ModelState.SetModelValue("EmailListDisplay", new ValueProviderResult(registrationValues.EmailListDisplay, "", null));

the ValueProviderResult takes care of the NullReference and you will get your validation messages on the page.

It's odd that the Html.TextBox entries don't seem to need this code.

Anyways I hope that helps you.

-Ed

Ed C