views:

3187

answers:

2

[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?

A: 

Try to convert your 2 fields into string to see if it's bindable?

linh1987
+16  A: 

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; }
}
roryf
I had always heard that public member variables were not binary equivalent to public properties (it is a breaking change), and now I guess I'm convinced.
Robert Harvey
This is the only complete example I have found. All of the other blog posts have pieces of the whole picture.
Robert Harvey