ASP.NET MVC model binding an IList<> parameter

roryf picture roryf · Mar 17, 2009 · Viewed 15.9k times · Source

[I solved this myself, see my answer for cause]

I'm having trouble getting form values for a IList<> argument in a controller method set properly.

My controller class looks like this:

public class ShoppingBasketController : Controller {

    public ActionResult Index() {
        return View();
    }

    [AcceptVerbs(HttpVerbs.Post)]
    public ActionResult Add(IList<ShoppingBasketItem> items) {
        Session["basket"] = items; // for testing
        return RedirectToAction("Index");
    }
}
public class ShoppingBasketItem {
     public int ItemID;
     public int ItemQuantity;
}

The slightly trimmed form:

<% using (Html.BeginForm("Add", "ShoppingBasket")) { %>
    <% int codeIndex = 0;
    foreach (Product product in products) { %>
        <%= Html.Hidden("items[" + codeIndex + "].ItemID", product.Id) %>
        <%= Html.TextBox("items[" + codeIndex + "].ItemQuantity", "0", new { size = "2"}) %>
        <% codeIndex++;
    }
} %>

Which produces markup like:

<form action="/Basket/Add" method="post">
    <input id="items[0]_ItemID" name="items[0].ItemID" type="hidden" value="1" />
    <input id="items[0]_ItemQuantity" name="items[0].ItemQuantity" size="2" type="text" value="0" />

    <input id="items[1]_ItemID" name="items[1].ItemID" type="hidden" value="2" />
    <input id="items[1]_ItemQuantity" name="items[2].ItemQuantity" size="2" type="text" value="0" />

    <input id="items[2]_ItemID" name="items[2].ItemID" type="hidden" value="3" />
    <input id="items[2]_ItemQuantity" name="items[2].ItemQuantity" size="2" type="text" value="0" />
</form>

I've checked the form values that get submitted and they are correct. The correct number of ShoppingBasketItems also get put into Session["basket"], however both the ItemID and ItemQuantity of each are zero. It appears to be correctly decoding the list of form values, but not picking up the properties themselves.

I'm using MVC RC2, and based on an article by Scott Hanselman I'm pretty sure my code is correct. Am I missing something?

Answer

roryf picture roryf · Mar 17, 2009

Solution

After downloading the MVC source I still couldn't see why it wouldn't work, so I presumed it must be something to do with the type I was attempting to bind. Sure enough, the values being member variables, as opposed to properties, was the culprit. This is because the model binder uses reflection to set properties, which it wasn't finding through the call to TypeDescriptor.GetProperties(Type).

Updating the value class to this solved it (after hours of hitting head off wall I should add!!):

public class ShoppingBasketItem {
    public int ItemID { get; set; }
    public int ItemQuantity { get; set; }
}