Note that there are some explanatory texts on larger screens.

plurals
  1. POChild Objects Fails to Bind
    text
    copied!<p>I am currently trying to incorporate Steve Sanderson's example code into my MVC 3 application and I'm having difficulty. </p> <p><a href="http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/" rel="nofollow noreferrer">http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/</a> </p> <p>The problem is that when I submit the form, the order details property of the order entity remains empty. I have looked at the following answer:</p> <p><a href="https://stackoverflow.com/questions/7153405/editing-a-variable-length-list-asp-net-mvc-3-style-with-table">Editing a Variable Length List, ASP.NET MVC 3 Style with Table</a></p> <p>but I still can't get it to work. I should point out that I'm relatively new to MVC and web development in general. I've looked through the site and failed to find any answer to this, so I apologise if there is one that I've missed. I have listed the relevant code below:</p> <p>Create.cshtml</p> <pre><code>@model NorthwindLight.Models.Order @using NorthwindLight.HtmlHelpers @{ ViewBag.Title = "Create"; AjaxOptions newOpts = new AjaxOptions(); newOpts.UpdateTargetId = "tabledata"; newOpts.InsertionMode = InsertionMode.InsertAfter; } &lt;h2&gt;Create&lt;/h2&gt; &lt;script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"&gt;&lt;/script&gt; &lt;script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")" type="text/javascript"&gt;&lt;/script&gt; @using (Html.BeginForm("Create", "Order", FormMethod.Post, new { name = "mainform", id = "mainform" })) { @Html.ValidationSummary(true) &lt;fieldset&gt; &lt;legend&gt;Order&lt;/legend&gt; &lt;div class="editor-label"&gt; @Html.LabelFor(model =&gt; model.CustomerId, "Customer") &lt;/div&gt; &lt;div class="editor-field"&gt; @Html.DropDownList("CustomerId", String.Empty) @Html.ValidationMessageFor(model =&gt; model.CustomerId) &lt;/div&gt; &lt;div class="editor-label"&gt; @Html.LabelFor(model =&gt; model.OrderDate) &lt;/div&gt; &lt;div class="editor-field"&gt; @Html.EditorFor(model =&gt; model.OrderDate) @Html.ValidationMessageFor(model =&gt; model.OrderDate) &lt;/div&gt; &lt;div class="editor-label"&gt; @Html.LabelFor(model =&gt; model.ShipName) &lt;/div&gt; &lt;div class="editor-field"&gt; @Html.EditorFor(model =&gt; model.ShipName) @Html.ValidationMessageFor(model =&gt; model.ShipName) &lt;/div&gt; &lt;div class="editor-label"&gt; @Html.LabelFor(model =&gt; model.ShipAddress) &lt;/div&gt; &lt;div class="editor-field"&gt; @Html.EditorFor(model =&gt; model.ShipAddress) @Html.ValidationMessageFor(model =&gt; model.ShipAddress) &lt;/div&gt; &lt;div class="editor-label"&gt; @Html.LabelFor(model =&gt; model.ShipCity) &lt;/div&gt; &lt;div class="editor-field"&gt; @Html.EditorFor(model =&gt; model.ShipCity) @Html.ValidationMessageFor(model =&gt; model.ShipCity) &lt;/div&gt; &lt;div class="editor-label"&gt; @Html.LabelFor(model =&gt; model.ShipRegion) &lt;/div&gt; &lt;div class="editor-field"&gt; @Html.EditorFor(model =&gt; model.ShipRegion) @Html.ValidationMessageFor(model =&gt; model.ShipRegion) &lt;/div&gt; &lt;div class="editor-label"&gt; @Html.LabelFor(model =&gt; model.ShipPostalCode) &lt;/div&gt; &lt;div class="editor-field"&gt; @Html.EditorFor(model =&gt; model.ShipPostalCode) @Html.ValidationMessageFor(model =&gt; model.ShipPostalCode) &lt;/div&gt; &lt;div class="editor-label"&gt; @Html.LabelFor(model =&gt; model.ShipCountry) &lt;/div&gt; &lt;div class="editor-field"&gt; @Html.EditorFor(model =&gt; model.ShipCountry) @Html.ValidationMessageFor(model =&gt; model.ShipCountry) &lt;/div&gt; &lt;/fieldset&gt; &lt;fieldset&gt; &lt;legend&gt;Order Details&lt;/legend&gt; &lt;br /&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Order&lt;/th&gt; &lt;th&gt;Product&lt;/th&gt; &lt;th&gt;Unit Price&lt;/th&gt; &lt;th&gt;Quantity&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody id="tabledata"&gt; @Html.Action("OrderDetailPartial") &lt;/tbody&gt; &lt;/table&gt; @Ajax.ActionLink("New Record", "OrderDetailPartial", newOpts) &lt;/fieldset&gt; } &lt;div&gt; &lt;a href="javascript:document.mainform.submit();"&gt;Create&lt;/a&gt; @Html.ActionLink("Back to List", "Index") &lt;/div&gt; </code></pre> <p>OrderDetailPartial.cshtml</p> <pre><code>@model NorthwindLight.Models.OrderDetail @using NorthwindLight.Models @using NorthwindLight.HtmlHelpers @{ Layout = null; var context = new NorthwindContext(); } &lt;tr&gt; &lt;td&gt; @using (Html.BeginCollectionItem("items")) { @:&lt;/td&gt; @:&lt;td&gt; @Html.Hidden("OrderId") @:&lt;/td&gt; @:&lt;td&gt; @Html.DropDownListFor(m =&gt; Model.ProductId, new SelectList (context.Products, "ProductId", "ProductName"), string.Empty) @:&lt;/td&gt; @:&lt;td&gt; @Html.EditorFor(m =&gt; Model.UnitPrice) @:&lt;/td&gt; @:&lt;td&gt; @Html.EditorFor(m =&gt; Model.Quantity) @:&lt;/td&gt; @:&lt;td&gt; &lt;a href="#" class="deleteRow"&gt;Delete&lt;/a&gt; } &lt;/td&gt; &lt;/tr&gt; </code></pre> <p>Action methods of OrderController</p> <pre><code>public ActionResult Create() { ViewBag.CustomerId = new SelectList(db.Customers, "CustomerId", "CompanyName"); return View(); } [HttpPost] public ActionResult Create(Order order) { if (ModelState.IsValid) { db.Orders.Add(order); db.SaveChanges(); return RedirectToAction("Index"); } ViewBag.CustomerId = new SelectList(db.Customers, "CustomerId", "CompanyName", order.CustomerId); return View(order); } public ViewResult OrderDetailPartial() { OrderDetail orderDetail = new OrderDetail(); return View(orderDetail); } </code></pre> <p>Order.cs</p> <pre><code>using System; using System.Collections.Generic; using System.Linq; using System.Web; namespace NorthwindLight.Models { public class Order { public int OrderId { get; set; } public int CustomerId { get; set; } public DateTime OrderDate { get; set; } public string ShipName { get; set; } public string ShipAddress { get; set; } public string ShipCity { get; set; } public string ShipRegion { get; set; } public string ShipPostalCode { get; set; } public string ShipCountry { get; set; } public virtual Customer Customer { get; set; } public byte[] RowVersion { get; set; } public virtual List&lt;OrderDetail&gt; OrderDetails { get; set; } public Order() { OrderDetails = new List&lt;OrderDetail&gt;(); } } } </code></pre> <p>OrderDetail.cs</p> <pre><code>using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.ComponentModel.DataAnnotations; namespace NorthwindLight.Models { public class OrderDetail { public int OrderId { get; set; } public int ProductId { get; set; } public decimal UnitPrice { get; set; } public int Quantity { get; set; } public virtual Order Order { get; set; } public virtual Product Product { get; set; } } } </code></pre> <p>The previous mentioned user stated that he got his example to work without having to change anything in the Html.BeginCollectionItem code that Steve Sanderson supplied, but I include that also (I should point out that this code item is totally beyond me at this time. I was hoping to use it as a black box).</p> <pre><code>using System; using System.Web.Mvc; using System.Web; using System.Collections.Generic; namespace NorthwindLight.HtmlHelpers { public static class HtmlPrefixScopeExtensions { private const string idsToReuseKey = "__htmlPrefixScopeExtensions_IdsToReuse_"; public static IDisposable BeginCollectionItem(this HtmlHelper html, string collectionName) { var idsToReuse = GetIdsToReuse(html.ViewContext.HttpContext, collectionName); string itemIndex = idsToReuse.Count &gt; 0 ? idsToReuse.Dequeue() : Guid.NewGuid().ToString(); // autocomplete="off" is needed to work around a very annoying Chrome behaviour whereby it reuses old values after the user clicks "Back", which causes the xyz.index and xyz[...] values to get out of sync. html.ViewContext.Writer.WriteLine(string.Format("&lt;input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" /&gt;", collectionName, html.Encode(itemIndex))); return BeginHtmlFieldPrefixScope(html, string.Format("{0}[{1}]", collectionName, itemIndex)); } public static IDisposable BeginHtmlFieldPrefixScope(this HtmlHelper html, string htmlFieldPrefix) { return new HtmlFieldPrefixScope(html.ViewData.TemplateInfo, htmlFieldPrefix); } private static Queue&lt;string&gt; GetIdsToReuse(HttpContextBase httpContext, string collectionName) { // We need to use the same sequence of IDs following a server-side validation failure, // otherwise the framework won't render the validation error messages next to each item. string key = idsToReuseKey + collectionName; var queue = (Queue&lt;string&gt;)httpContext.Items[key]; if (queue == null) { httpContext.Items[key] = queue = new Queue&lt;string&gt;(); var previouslyUsedIds = httpContext.Request[collectionName + ".index"]; if (!string.IsNullOrEmpty(previouslyUsedIds)) foreach (string previouslyUsedId in previouslyUsedIds.Split(',')) queue.Enqueue(previouslyUsedId); } return queue; } private class HtmlFieldPrefixScope : IDisposable { private readonly TemplateInfo templateInfo; private readonly string previousHtmlFieldPrefix; public HtmlFieldPrefixScope(TemplateInfo templateInfo, string htmlFieldPrefix) { this.templateInfo = templateInfo; previousHtmlFieldPrefix = templateInfo.HtmlFieldPrefix; templateInfo.HtmlFieldPrefix = htmlFieldPrefix; } public void Dispose() { templateInfo.HtmlFieldPrefix = previousHtmlFieldPrefix; } } } } </code></pre> <p>I have also included below a sample of the html that is created from the above code.</p> <pre><code>&lt;html class=" js flexbox canvas canvastext webgl no-touch geolocation postmessage no-websqldatabase indexeddb hashchange history draganddrop websockets rgba hsla multiplebgs backgroundsize borderimage borderradius boxshadow textshadow opacity cssanimations csscolumns cssgradients no-cssreflections csstransforms csstransforms3d csstransitions fontface video audio localstorage sessionstorage webworkers applicationcache svg inlinesvg smil svgclippaths"&gt;&lt;head&gt; &lt;meta charset="utf-8"&gt; &lt;title&gt;Create&lt;/title&gt; &lt;link type="text/css" rel="stylesheet" href="/Content/Site.css"&gt; &lt;script type="text/javascript" src="/Scripts/jquery-1.5.1.min.js"&gt;&lt;/script&gt; &lt;script type="text/javascript" src="/Scripts/modernizr-1.7.min.js"&gt;&lt;/script&gt; &lt;script type="text/javascript" src="/Scripts/DeleteRow.js"&gt;&lt;/script&gt; &lt;script type="text/javascript" src="/Scripts/jquery.unobtrusive-ajax.js"&gt;&lt;/script&gt; &lt;/head&gt; &lt;body&gt; &lt;div class="page"&gt; &lt;header&gt; &lt;div id="title"&gt; &lt;h1&gt;My MVC Application&lt;/h1&gt; &lt;/div&gt; &lt;div id="logindisplay"&gt; [ &lt;a href="/Account/LogOn"&gt;Log On&lt;/a&gt; ] &lt;/div&gt; &lt;nav&gt; &lt;ul id="menu"&gt; &lt;li&gt;&lt;a href="/"&gt;Home&lt;/a&gt;&lt;/li&gt; &lt;li&gt;&lt;a href="/Home/About"&gt;About&lt;/a&gt;&lt;/li&gt; &lt;/ul&gt; &lt;/nav&gt; &lt;/header&gt; &lt;section id="main"&gt; &lt;h2&gt;Create&lt;/h2&gt; &lt;script type="text/javascript" src="/Scripts/jquery.validate.min.js"&gt;&lt;/script&gt; &lt;script type="text/javascript" src="/Scripts/jquery.validate.unobtrusive.min.js"&gt;&lt;/script&gt; &lt;form name="mainform" method="post" id="mainform" action="/Order/Create"&gt; &lt;fieldset&gt; &lt;legend&gt;Order&lt;/legend&gt; &lt;div class="editor-label"&gt; &lt;label for="CustomerId"&gt;Customer&lt;/label&gt; &lt;/div&gt; &lt;div class="editor-field"&gt; &lt;select name="CustomerId" id="CustomerId" class="valid"&gt;&lt;option value=""&gt;&lt;/option&gt; &lt;option value="1"&gt;One Company&lt;/option&gt; &lt;option value="2"&gt;Two Company&lt;/option&gt; &lt;option value="3"&gt;Three Company&lt;/option&gt; &lt;/select&gt; &lt;span data-valmsg-replace="true" data-valmsg-for="CustomerId" class="field-validation-valid"&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class="editor-label"&gt; &lt;label for="OrderDate"&gt;OrderDate&lt;/label&gt; &lt;/div&gt; &lt;div class="editor-field"&gt; &lt;input type="text" value="" name="OrderDate" id="OrderDate" data-val-required="The OrderDate field is required." data-val="true" class="text-box single-line valid"&gt; &lt;span data-valmsg-replace="true" data-valmsg-for="OrderDate" class="field-validation-valid"&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class="editor-label"&gt; &lt;label for="ShipName"&gt;ShipName&lt;/label&gt; &lt;/div&gt; &lt;div class="editor-field"&gt; &lt;input type="text" value="" name="ShipName" id="ShipName" class="text-box single-line valid"&gt; &lt;span data-valmsg-replace="true" data-valmsg-for="ShipName" class="field-validation-valid"&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class="editor-label"&gt; &lt;label for="ShipAddress"&gt;ShipAddress&lt;/label&gt; &lt;/div&gt; &lt;div class="editor-field"&gt; &lt;input type="text" value="" name="ShipAddress" id="ShipAddress" class="text-box single-line valid"&gt; &lt;span data-valmsg-replace="true" data-valmsg-for="ShipAddress" class="field-validation-valid"&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class="editor-label"&gt; &lt;label for="ShipCity"&gt;ShipCity&lt;/label&gt; &lt;/div&gt; &lt;div class="editor-field"&gt; &lt;input type="text" value="" name="ShipCity" id="ShipCity" class="text-box single-line valid"&gt; &lt;span data-valmsg-replace="true" data-valmsg-for="ShipCity" class="field-validation-valid"&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class="editor-label"&gt; &lt;label for="ShipRegion"&gt;ShipRegion&lt;/label&gt; &lt;/div&gt; &lt;div class="editor-field"&gt; &lt;input type="text" value="" name="ShipRegion" id="ShipRegion" class="text-box single-line valid"&gt; &lt;span data-valmsg-replace="true" data-valmsg-for="ShipRegion" class="field-validation-valid"&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class="editor-label"&gt; &lt;label for="ShipPostalCode"&gt;ShipPostalCode&lt;/label&gt; &lt;/div&gt; &lt;div class="editor-field"&gt; &lt;input type="text" value="" name="ShipPostalCode" id="ShipPostalCode" class="text-box single-line valid"&gt; &lt;span data-valmsg-replace="true" data-valmsg-for="ShipPostalCode" class="field-validation-valid"&gt;&lt;/span&gt; &lt;/div&gt; &lt;div class="editor-label"&gt; &lt;label for="ShipCountry"&gt;ShipCountry&lt;/label&gt; &lt;/div&gt; &lt;div class="editor-field"&gt; &lt;input type="text" value="" name="ShipCountry" id="ShipCountry" class="text-box single-line valid"&gt; &lt;span data-valmsg-replace="true" data-valmsg-for="ShipCountry" class="field-validation-valid"&gt;&lt;/span&gt; &lt;/div&gt; &lt;/fieldset&gt; &lt;fieldset&gt; &lt;legend&gt;Order Details&lt;/legend&gt; &lt;br&gt; &lt;table&gt; &lt;thead&gt; &lt;tr&gt; &lt;th&gt;Product&lt;/th&gt; &lt;th&gt;Unit Price&lt;/th&gt; &lt;th&gt;Quantity&lt;/th&gt; &lt;th&gt;&lt;/th&gt; &lt;/tr&gt; &lt;/thead&gt; &lt;tbody id="tabledata"&gt; &lt;tr&gt; &lt;td&gt; &lt;input type="hidden" value="96300b05-ec2e-4058-b380-b67976c6ae41" autocomplete="off" name="items.index"&gt; &lt;select name="items[96300b05-ec2e-4058-b380-b67976c6ae41].ProductId" id="items_96300b05-ec2e-4058-b380-b67976c6ae41__ProductId" data-val-required="The ProductId field is required." data-val-number="The field ProductId must be a number." data-val="true" class="valid"&gt;&lt;option value=""&gt;&lt;/option&gt; &lt;option value="1"&gt;One Product&lt;/option&gt; &lt;option value="2"&gt;Two Product&lt;/option&gt; &lt;option value="3"&gt;Three Product&lt;/option&gt; &lt;option value="4"&gt;Four Product&lt;/option&gt; &lt;/select&gt; &lt;/td&gt; &lt;td&gt; &lt;input type="text" value="0.00" name="items[96300b05-ec2e-4058-b380-b67976c6ae41].UnitPrice" id="items_96300b05-ec2e-4058-b380-b67976c6ae41__UnitPrice" data-val-required="The UnitPrice field is required." data-val-number="The field UnitPrice must be a number." data-val="true" class="text-box single-line valid"&gt; &lt;/td&gt; &lt;td&gt; &lt;input type="text" value="0" name="items[96300b05-ec2e-4058-b380-b67976c6ae41].Quantity" id="items_96300b05-ec2e-4058-b380-b67976c6ae41__Quantity" data-val-required="The Quantity field is required." data-val-number="The field Quantity must be a number." data-val="true" class="text-box single-line valid"&gt; &lt;/td&gt; &lt;td&gt; &lt;a class="deleteRow" href="#"&gt;Delete&lt;/a&gt; &lt;/td&gt; &lt;td&gt;&lt;input type="hidden" value="" name="items[96300b05-ec2e-4058-b380-b67976c6ae41].OrderId" id="items_96300b05-ec2e-4058-b380-b67976c6ae41__OrderId" data-val-required="The OrderId field is required." data-val-number="The field OrderId must be a number." data-val="true"&gt; &lt;/td&gt; &lt;/tr&gt; &lt;tr&gt; &lt;td&gt; &lt;input type="hidden" value="a7286ad0-8389-4613-aefe-120a54f57318" autocomplete="off" name="items.index"&gt; &lt;select name="items[a7286ad0-8389-4613-aefe-120a54f57318].ProductId" id="items_a7286ad0-8389-4613-aefe-120a54f57318__ProductId" class="valid"&gt;&lt;option value=""&gt;&lt;/option&gt; &lt;option value="1"&gt;One Product&lt;/option&gt; &lt;option value="2"&gt;Two Product&lt;/option&gt; &lt;option value="3"&gt;Three Product&lt;/option&gt; &lt;option value="4"&gt;Four Product&lt;/option&gt; &lt;/select&gt; &lt;/td&gt; &lt;td&gt; &lt;input type="text" value="0.00" name="items[a7286ad0-8389-4613-aefe-120a54f57318].UnitPrice" id="items_a7286ad0-8389-4613-aefe-120a54f57318__UnitPrice" class="text-box single-line valid"&gt; &lt;/td&gt; &lt;td&gt; &lt;input type="text" value="0" name="items[a7286ad0-8389-4613-aefe-120a54f57318].Quantity" id="items_a7286ad0-8389-4613-aefe-120a54f57318__Quantity" class="text-box single-line valid"&gt; &lt;/td&gt; &lt;td&gt; &lt;a class="deleteRow" href="#"&gt;Delete&lt;/a&gt; &lt;/td&gt; &lt;td&gt;&lt;input type="hidden" value="" name="items[a7286ad0-8389-4613-aefe-120a54f57318].OrderId" id="items_a7286ad0-8389-4613-aefe-120a54f57318__OrderId"&gt; &lt;/td&gt; &lt;/tr&gt;&lt;/tbody&gt; &lt;/table&gt; &lt;a href="/Order/OrderDetailPartial" data-ajax-update="#tabledata" data-ajax-mode="after" data-ajax="true"&gt;New Record&lt;/a&gt; &lt;/fieldset&gt; &lt;/form&gt; &lt;div&gt; &lt;a href="javascript:document.mainform.submit();"&gt;Create&lt;/a&gt; &lt;a href="/Order"&gt;Cancel&lt;/a&gt; &lt;/div&gt; &lt;/section&gt; &lt;footer&gt; &lt;/footer&gt; &lt;/div&gt; &lt;/body&gt;&lt;/html&gt; </code></pre> <p>What happens is that when I press submit, the Order entity has its properties caught by the model binder, but the List has a count of 0.</p> <p>Any help would be greatly appreciated.</p> <p>Edit: Is there anything wrong with the way that I've asked this question. It's not that I'm impatient, but I thought that I would have had a comment by now. If anyone has any way of this question being improved, please let me know.</p> <p>Edit: After waiting a week and getting no responses, I've been trying to get around using the defaultbinder by changing the create method to the following: </p> <pre><code>[HttpPost] public ActionResult Create(FormCollection formCollection) { Order order = new Order(); UpdateModel(order); order.OrderDetails = new List&lt;OrderDetail&gt;(); UpdateModel(order.OrderDetails); if (ModelState.IsValid) { db.Orders.Add(order); db.SaveChanges(); return RedirectToAction("Index"); } ViewBag.CustomerId = new SelectList(db.Customers, "CustomerId", "CompanyName", order.CustomerId); return View(order); } </code></pre> <p>This has worked to a certain extent, in that I now get the following keys in the FormCollection object: </p> <pre><code>[0] = "CustomerId" [1] = "OrderDate" [2] = "ShipName" [3] = "ShipAddress" [4] = "ShipCity" [5] = "ShipRegion" [6] = "ShipPostalCode" [7] = "ShipCountry" [8] = "items.index" [9] = "items[c2e8e9de-f81a-4b9b-9763-55dda8acd892].ProductId" [10] = "items[c2e8e9de-f81a-4b9b-9763-55dda8acd892].UnitPrice" [11] = "items[c2e8e9de-f81a-4b9b-9763-55dda8acd892].Quantity" [12] = "items[c2e8e9de-f81a-4b9b-9763-55dda8acd892].OrderId" [13] = "items[dafbddb2-efb0-42b8-ac03-177e29e70f2b].ProductId" [14] = "items[dafbddb2-efb0-42b8-ac03-177e29e70f2b].UnitPrice" [15] = "items[dafbddb2-efb0-42b8-ac03-177e29e70f2b].Quantity" [16] = "items[dafbddb2-efb0-42b8-ac03-177e29e70f2b].OrderId" </code></pre> <p>I got the idea from this site <a href="http://goneale.com/2009/07/27/updating-multiple-child-objects-and-or-collections-in-asp-net-mvc-views/" rel="nofollow noreferrer">http://goneale.com/2009/07/27/updating-multiple-child-objects-and-or-collections-in-asp-net-mvc-views/</a> but I still do not know how to get the child values into the OrderDetails property of the order object. </p> <p>Again, any comments would be greatly appreciated, as I'm not even sure that I'm asking this question in the right way.</p> <p>Edit: I have received an answer from another forum, so I thought that I'd add it to this post. My error was that I'd entered "items" to BeginCollectionItem, when I should have entered "OrderDetails". The code for the partial view is now as follows:</p> <pre><code> @model NorthwindLight.Models.OrderDetail @using NorthwindLight.Models @using NorthwindLight.HtmlHelpers @{ Layout = null; var context = new NorthwindContext(); } &lt;tr&gt; @using (Html.BeginCollectionItem("OrderDetails")) { @:&lt;td&gt; @Html.DropDownListFor(m =&gt; Model.ProductId, new SelectList(context.Products, "ProductId", "ProductName"), string.Empty) @:&lt;/td&gt; @:&lt;td&gt; @Html.EditorFor(m =&gt; Model.UnitPrice) @:&lt;/td&gt; @:&lt;td&gt; @Html.EditorFor(m =&gt; Model.Quantity) @:&lt;/td&gt; @:&lt;td&gt; &lt;a href="#" class="deleteRow"&gt;Delete&lt;/a&gt; @:&lt;/td&gt; @:&lt;td&gt;@Html.HiddenFor(m =&gt; m.OrderId); } &lt;/td&gt; &lt;/tr&gt; </code></pre> <p>It's obvious now that I've seen it, but I couldn't see the wood for the trees. Many thanks to all those that looked at the code.</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload