我目前正在尝试将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; }
}
}
前面提到过的用户说他没有在Steve Sanderson提供的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>
当我按下提交时,Order实体的属性被模型绑定器捕获,但List的计数为0。
非常感谢任何帮助。
编辑:我问这个问题的方式有什么不对。并不是说我不耐烦,但我想我现在已经有了评论。如果有人对此问题有任何改进,请告诉我。
编辑:在等待一周并且没有得到任何回复后,我一直试图通过将create方法更改为以下来使用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/得到了这个想法,但我仍然不知道如何将子值转换为订单对象的OrderDetails属性。
同样,任何评论都会非常感激,因为我甚至不确定我是否以正确的方式提出这个问题。
编辑:我收到了另一个论坛的答案,所以我想我会把它添加到这篇文章中。我的错误是我输入“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>
现在很明显我已经看过了,但我看不到木头的树木了。非常感谢所有关注代码的人。
答案 0 :(得分:4)
我收到另一个四分钟的答案,我想我会在这里发布。问题出在下面代码中的字符串'items'中:
@using (Html.BeginCollectionItem("items"))
这意味着HTML中的OrderDetails行以项目开头。
[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绑定