2012-03-25 83 views
0

我目前正试图将Steve Sanderson的示例代码合并到我的MVC 3应用程序中,并且遇到了困难。子对象无法绑定

http://blog.stevensanderson.com/2010/01/28/editing-a-variable-length-list-aspnet-mvc-2-style/

的问题是,当我提交表单,订单实体的订单详情财产保持为空。我已经看过了以下的答案:

Editing a Variable Length List, ASP.NET MVC 3 Style with Table

,但我仍然无法得到它的工作。我应该指出,我对MVC和Web开发一般都比较陌生。我浏览过网站,未能找到任何答案,所以我很抱歉,如果有一个我错过了。我在下面列出了相关代码:

Create.cshtml

@model NorthwindLight.Models.Order 
     @using NorthwindLight.HtmlHelpers 

@{ 
    ViewBag.Title = "Create"; 
    AjaxOptions newOpts = new AjaxOptions(); 
    newOpts.UpdateTargetId = "tabledata"; 
    newOpts.InsertionMode = InsertionMode.InsertAfter; 
} 

<h2>Create</h2> 

<script src="@Url.Content("~/Scripts/jquery.validate.min.js")" type="text/javascript"></script> 
<script src="@Url.Content("~/Scripts/jquery.validate.unobtrusive.min.js")"  type="text/javascript"></script> 

@using (Html.BeginForm("Create", "Order", FormMethod.Post, new { name = "mainform", id = "mainform" })) { 
    @Html.ValidationSummary(true) 
    <fieldset> 
     <legend>Order</legend> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.CustomerId, "Customer") 
     </div> 
     <div class="editor-field"> 
      @Html.DropDownList("CustomerId", String.Empty) 
      @Html.ValidationMessageFor(model => model.CustomerId) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.OrderDate) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.OrderDate) 
      @Html.ValidationMessageFor(model => model.OrderDate) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.ShipName) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.ShipName) 
      @Html.ValidationMessageFor(model => model.ShipName) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.ShipAddress) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.ShipAddress) 
      @Html.ValidationMessageFor(model => model.ShipAddress) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.ShipCity) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.ShipCity) 
      @Html.ValidationMessageFor(model => model.ShipCity) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.ShipRegion) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.ShipRegion) 
      @Html.ValidationMessageFor(model => model.ShipRegion) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.ShipPostalCode) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.ShipPostalCode) 
      @Html.ValidationMessageFor(model => model.ShipPostalCode) 
     </div> 

     <div class="editor-label"> 
      @Html.LabelFor(model => model.ShipCountry) 
     </div> 
     <div class="editor-field"> 
      @Html.EditorFor(model => model.ShipCountry) 
      @Html.ValidationMessageFor(model => model.ShipCountry) 
     </div> 
    </fieldset> 
    <fieldset> 
    <legend>Order Details</legend> 
    <br /> 
    <table> 
     <thead> 
      <tr> 
       <th>Order</th> 
       <th>Product</th> 
       <th>Unit Price</th> 
       <th>Quantity</th> 
       <th></th> 
      </tr> 
     </thead> 
     <tbody id="tabledata"> 
      @Html.Action("OrderDetailPartial") 
     </tbody> 
    </table> 
    @Ajax.ActionLink("New Record", "OrderDetailPartial", newOpts) 
</fieldset> 
} 

<div> 
    <a href="javascript:document.mainform.submit();">Create</a> 
    @Html.ActionLink("Back to List", "Index") 
</div> 

OrderDetailPartial.cshtml

@model NorthwindLight.Models.OrderDetail 
     @using NorthwindLight.Models 
     @using NorthwindLight.HtmlHelpers 

@{ 
    Layout = null; 
    var context = new NorthwindContext(); 
} 

<tr> 
    <td> 
    @using (Html.BeginCollectionItem("items")) 
    { 
     @:</td> 
     @:<td> 
      @Html.Hidden("OrderId") 
     @:</td> 
     @:<td> 
      @Html.DropDownListFor(m => Model.ProductId, new SelectList (context.Products, "ProductId", "ProductName"), string.Empty) 
     @:</td> 
     @:<td> 
      @Html.EditorFor(m => Model.UnitPrice) 
     @:</td> 
     @:<td> 
      @Html.EditorFor(m => Model.Quantity) 
     @:</td> 
     @:<td> 
      <a href="#" class="deleteRow">Delete</a> 
    } 
    </td> 
</tr> 

OrderController的操作方法

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); 
} 

Order.cs

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<OrderDetail> OrderDetails { get; set; } 

     public Order() 
     { 
      OrderDetails = new List<OrderDetail>(); 
     } 
    } 
} 

OrderDetail.cs

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; } 
    } 
} 

先前提到的用户说,他得到了他的例子,而无需改变,史蒂夫·桑德森提供的Html.BeginCollectionItem代码什么工作,但我有同样(我应该指出这个代码项目目前完全超出了我的想象。我曾希望将它用作黑匣子)。

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 > 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("<input type=\"hidden\" name=\"{0}.index\" autocomplete=\"off\" value=\"{1}\" />", 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<string> 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<string>)httpContext.Items[key]; 
     if (queue == null) { 
      httpContext.Items[key] = queue = new Queue<string>(); 
      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; 
     } 
    } 
} 
} 

我还列出了从上面的代码创建的html样本。

<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"><head> 
<meta charset="utf-8"> 
<title>Create</title> 
<link type="text/css" rel="stylesheet" href="/Content/Site.css"> 
<script type="text/javascript" src="/Scripts/jquery-1.5.1.min.js"></script> 
<script type="text/javascript" src="/Scripts/modernizr-1.7.min.js"></script> 
<script type="text/javascript" src="/Scripts/DeleteRow.js"></script> 
<script type="text/javascript" src="/Scripts/jquery.unobtrusive-ajax.js"></script> 
</head> 
<body> 
<div class="page"> 
    <header> 
     <div id="title"> 
      <h1>My MVC Application</h1> 
     </div> 
     <div id="logindisplay"> 
       [ <a href="/Account/LogOn">Log On</a> ] 

     </div> 
     <nav> 
      <ul id="menu"> 
       <li><a href="/">Home</a></li> 
       <li><a href="/Home/About">About</a></li> 
      </ul> 
     </nav> 
    </header> 
    <section id="main"> 


<h2>Create</h2> 

<script type="text/javascript" src="/Scripts/jquery.validate.min.js"></script> 
<script type="text/javascript" src="/Scripts/jquery.validate.unobtrusive.min.js"></script> 

<form name="mainform" method="post" id="mainform" action="/Order/Create"> <fieldset> 
    <legend>Order</legend> 

    <div class="editor-label"> 
     <label for="CustomerId">Customer</label> 
    </div> 
    <div class="editor-field"> 
     <select name="CustomerId" id="CustomerId" class="valid"><option value=""></option> 
<option value="1">One Company</option> 
<option value="2">Two Company</option> 
<option value="3">Three Company</option> 
</select> 
     <span data-valmsg-replace="true" data-valmsg-for="CustomerId" class="field-validation-valid"></span> 
    </div> 

    <div class="editor-label"> 
     <label for="OrderDate">OrderDate</label> 
    </div> 
    <div class="editor-field"> 
     <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"> 
     <span data-valmsg-replace="true" data-valmsg-for="OrderDate" class="field-validation-valid"></span> 
    </div> 

    <div class="editor-label"> 
     <label for="ShipName">ShipName</label> 
    </div> 
    <div class="editor-field"> 
     <input type="text" value="" name="ShipName" id="ShipName" class="text-box single-line valid"> 
     <span data-valmsg-replace="true" data-valmsg-for="ShipName" class="field-validation-valid"></span> 
    </div> 

    <div class="editor-label"> 
     <label for="ShipAddress">ShipAddress</label> 
    </div> 
    <div class="editor-field"> 
     <input type="text" value="" name="ShipAddress" id="ShipAddress" class="text-box single-line valid"> 
     <span data-valmsg-replace="true" data-valmsg-for="ShipAddress" class="field-validation-valid"></span> 
    </div> 

    <div class="editor-label"> 
     <label for="ShipCity">ShipCity</label> 
    </div> 
    <div class="editor-field"> 
     <input type="text" value="" name="ShipCity" id="ShipCity" class="text-box single-line valid"> 
     <span data-valmsg-replace="true" data-valmsg-for="ShipCity" class="field-validation-valid"></span> 
    </div> 

    <div class="editor-label"> 
     <label for="ShipRegion">ShipRegion</label> 
    </div> 
    <div class="editor-field"> 
     <input type="text" value="" name="ShipRegion" id="ShipRegion" class="text-box single-line valid"> 
     <span data-valmsg-replace="true" data-valmsg-for="ShipRegion" class="field-validation-valid"></span> 
    </div> 

    <div class="editor-label"> 
     <label for="ShipPostalCode">ShipPostalCode</label> 
    </div> 
    <div class="editor-field"> 
     <input type="text" value="" name="ShipPostalCode" id="ShipPostalCode" class="text-box single-line valid"> 
     <span data-valmsg-replace="true" data-valmsg-for="ShipPostalCode" class="field-validation-valid"></span> 
    </div> 

    <div class="editor-label"> 
     <label for="ShipCountry">ShipCountry</label> 
    </div> 
    <div class="editor-field"> 
     <input type="text" value="" name="ShipCountry" id="ShipCountry" class="text-box single-line valid"> 
     <span data-valmsg-replace="true" data-valmsg-for="ShipCountry" class="field-validation-valid"></span> 
    </div> 
</fieldset> 
<fieldset> 
<legend>Order Details</legend> 
<br> 
<table> 
    <thead> 
     <tr> 
      <th>Product</th> 
      <th>Unit Price</th> 
      <th>Quantity</th> 
      <th></th> 
     </tr> 
    </thead> 
    <tbody id="tabledata"> 


<tr> 
<td> 
<input type="hidden" value="96300b05-ec2e-4058-b380-b67976c6ae41" autocomplete="off" name="items.index"> 
<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"><option value=""></option> 
<option value="1">One Product</option> 
<option value="2">Two Product</option> 
<option value="3">Three Product</option> 
<option value="4">Four Product</option> 
</select>  </td> 
    <td> 
<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">  </td> 
    <td> 
<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">  </td> 
    <td> 
     <a class="deleteRow" href="#">Delete</a> 
    </td> 
    <td><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"> 
</td> 
</tr> 


<tr> 
<td> 
<input type="hidden" value="a7286ad0-8389-4613-aefe-120a54f57318" autocomplete="off" name="items.index"> 
<select name="items[a7286ad0-8389-4613-aefe-120a54f57318].ProductId" id="items_a7286ad0-8389-4613-aefe-120a54f57318__ProductId" class="valid"><option value=""></option> 
<option value="1">One Product</option> 
<option value="2">Two Product</option> 
<option value="3">Three Product</option> 
<option value="4">Four Product</option> 
</select>  </td> 
    <td> 
<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">  </td> 
    <td> 
<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">   </td> 
    <td> 
     <a class="deleteRow" href="#">Delete</a> 
    </td> 
    <td><input type="hidden" value="" name="items[a7286ad0-8389-4613-aefe-120a54f57318].OrderId" id="items_a7286ad0-8389-4613-aefe-120a54f57318__OrderId"> 
</td> 
</tr></tbody> 
</table> 
<a href="/Order/OrderDetailPartial" data-ajax-update="#tabledata" data-ajax-mode="after" data-ajax="true">New Record</a> 
</fieldset> 
</form> 
<div> 
<a href="javascript:document.mainform.submit();">Create</a> 
<a href="/Order">Cancel</a> 
</div> 

    </section> 
    <footer> 
    </footer> 
</div> 


</body></html> 

什么情况是,当我按下提交订单实体具有其属性通过模型绑定抓住了,但名单已经0

计数任何帮助将不胜感激。

编辑:我问过这个问题的方式有什么问题吗?这并不是我不耐烦,但我认为现在我会发表评论。如果有人有任何改善这个问题的方法,请告诉我。

编辑:等待一个星期,没有得到响应的后,我一直在试图通过改变创建方法如下避开使用defaultbinder:

[HttpPost] 
    public ActionResult Create(FormCollection formCollection) 
    { 
     Order order = new Order(); 
     UpdateModel(order); 
     order.OrderDetails = new List<OrderDetail>(); 
     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); 
    } 

这工作在一定程度上,在我现在得到以下键在的FormCollection对象:

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

我的想法从这个网站http://goneale.com/2009/07/27/updating-multiple-child-objects-and-or-collections-in-asp-net-mvc-views/,但我还是不知道如何让孩子值到订单对象的订单明细财产。

再次,任何意见将不胜感激,因为我甚至不确定我是以正确的方式问这个问题。

编辑:我收到了来自另一个论坛的答案,所以我认为我会将其添加到该帖子中。我的错误是我输入“Items”到BeginCollectionItem,当我输入“OrderDetails”时。对于局部视图代码如下内容:

@model NorthwindLight.Models.OrderDetail 
     @using NorthwindLight.Models 
     @using NorthwindLight.HtmlHelpers 

@{ 
    Layout = null; 
    var context = new NorthwindContext(); 
} 

<tr> 
    @using (Html.BeginCollectionItem("OrderDetails")) 
    { 
     @:<td> 
      @Html.DropDownListFor(m => Model.ProductId, new SelectList(context.Products, "ProductId", "ProductName"), string.Empty) 
     @:</td> 
     @:<td> 
      @Html.EditorFor(m => Model.UnitPrice) 
     @:</td> 
     @:<td> 
      @Html.EditorFor(m => Model.Quantity) 
     @:</td> 
     @:<td> 
      <a href="#" class="deleteRow">Delete</a> 
     @:</td> 
     @:<td>@Html.HiddenFor(m => m.OrderId); 
    } 
    </td> 
</tr> 

现在,我已经看到这是显而易见的,但我看不到树木,不见森林。非常感谢所有查看代码的人。

回答

4

我收到了另一个4m的答案,我以为我会在这里发布。问题出现在以下代码中的字符串'items'中:

@using (Html.BeginCollectionItem("items")) 

这意味着HTML中的OrderDetails行是以items开始的。

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

这阻止了ModelBinder检测到它们是Order对象的子对象。当代码改成这样:

@using (Html.BeginCollectionItem("OrderDetails")) 

的HTML生成如下:

[9] = "OrderDetails[c2e8e9de-f81a-4b9b-9763-55dda8acd892].ProductId" 
[10] = "OrderDetails[c2e8e9de-f81a-4b9b-9763-55dda8acd892].UnitPrice" 
[11] = "OrderDetails[c2e8e9de-f81a-4b9b-9763-55dda8acd892].Quantity" 
[12] = "OrderDetails[c2e8e9de-f81a-4b9b-9763-55dda8acd892].OrderId" 
[13] = "OrderDetails[dafbddb2-efb0-42b8-ac03-177e29e70f2b].ProductId" 
[14] = "OrderDetails[dafbddb2-efb0-42b8-ac03-177e29e70f2b].UnitPrice" 
[15] = "OrderDetails[dafbddb2-efb0-42b8-ac03-177e29e70f2b].Quantity" 
[16] = "OrderDetails[dafbddb2-efb0-42b8-ac03-177e29e70f2b].OrderId" 

现在的代码工作正常,子对象由ModelBinder的

约束