27

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

1
  • Is there a way to make this work with UI templates, using an EditorFor(m => m.product) for each iteration in Products? Commented Feb 7, 2010 at 19:30

1 Answer 1

35

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; }
}
Sign up to request clarification or add additional context in comments.

3 Comments

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.
This is the only complete example I have found. All of the other blog posts have pieces of the whole picture.
Thank you so much! I was banging my head on the table for hours trying to figure out what was wrong.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.