在我的MedicalProductController
中,我尝试让我的Edit
操作能够在一个页面上编辑多个对象。为此,我计划使用HTTPPOST
编辑操作方法接收IEnumerable<MedicalProduct>
,而不是脚手架为我设置的MedicalProduct
。
当我点击“保存”提交一些更改时,我在行ArguementNullException
上未处理_db.Entry(productList).State = EntityState.Modified;
,我不明白为什么它为空。
public class MedicalProductController : Controller
{
private MvcMedicalStoreDb _db = new MvcMedicalStoreDb();
// some code omitted for brevity
public ActionResult Edit(int id = 0)
{
MedicalProduct product = _db.Products.Find(id);
if (product == null)
{
return HttpNotFound();
}
var productList = new List<MedicalProduct> { product };
var viewModel = GetMedicalProductViewModelList(productList);
return View(viewModel);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(IEnumerable<MedicalProduct> productList)
{
if (ModelState.IsValid)
{
_db.Entry(productList).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
//var productList = new List<MedicalProduct> { product };
var viewModel = GetMedicalProductViewModelList(productList);
return View(viewModel);
}
}
@model IEnumerable<MvcMedicalStore.Models.MedicalProductViewModel>
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
@foreach (var modelItem in Model)
{
@Html.HiddenFor(item => modelItem.ID)
<div class="editor-label">
@Html.LabelFor(item => modelItem.Name)
</div>
<div class="editor-field">
@Html.EditorFor(item => modelItem.Name)
@Html.ValidationMessageFor(item => modelItem.Name)
</div>
<div class="editor-label">
@Html.LabelFor(item => modelItem.Price)
</div>
<div class="editor-field">
@Html.EditorFor(item => modelItem.Price)
@Html.ValidationMessageFor(item => modelItem.Price)
</div>
<div class="editor-label">
@Html.LabelFor(item => modelItem.BrandName)
</div>
<div class="editor-field">
@Html.DropDownListFor(item => modelItem.BrandName, modelItem.BrandSelectListItem)
@Html.ValidationMessageFor(item => modelItem.BrandName)
</div>
}
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
答案 0 :(得分:2)
在我看来,模型绑定器无法绑定到您的集合,这将导致它null
。它之所以这样做是因为你没有为每个元素指定索引。这意味着MVC无法确定如何正确绑定它们。
我已经弄明白为什么这个答案的最后一次修订不起作用。首先,IEnumerable<T>
没有直接索引器。相反,您可以使用Model.ElementAt(i).ID
来访问ID
属性。但是,这实际上无法解决模型绑定问题,因为由于某些原因,这不会为生成的name
字段生成<input>
属性的正确索引。 (更多内容见下文。)
有两种方法可以解决这个问题。第一种方法是将List
传递给视图,而不是IEnumerable
,然后访问前面显示的字段。但是,更好的方法是创建EditorTemplate
。这将更容易,因为它可以节省您必须更改生成视图模型的现有方法。所以你需要按照以下步骤操作:
EditorTemplates
文件夹(例如,如果您的视图为Home\Index.cshtml
,请创建文件夹Home\EditorTemplates
)。MedicalProductViewModel
)。您最终会得到以下信息:
@model MedicalProductViewModel
@Html.HiddenFor(item => Model.ID)
<div class="editor-label">
@Html.LabelFor(item => Model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(item => Model.Name)
@Html.ValidationMessageFor(item => Model.Name)
</div>
<div class="editor-label">
@Html.LabelFor(item => Model.Price)
</div>
<div class="editor-field">
@Html.EditorFor(item => Model.Price)
@Html.ValidationMessageFor(item => Model.Price)
</div>
<div class="editor-label">
@Html.LabelFor(item => Model.BrandName)
</div>
<div class="editor-field">
@Html.DropDownListFor(item => Model.BrandName, Model.BrandSelectListItem)
@Html.ValidationMessageFor(item => Model.BrandName)
</div>
请注意我们不再使用任何索引表示法来访问模型属性。
现在,在Edit.cshtml
视图中,您将完成此任务:
@model IEnumerable<MvcMedicalStore.Models.MedicalProductViewModel>
@{
ViewBag.Title = "Edit";
}
<h2>Edit</h2>
@using (Html.BeginForm()) {
@Html.AntiForgeryToken()
@Html.ValidationSummary(true)
<fieldset>
<legend>MedicalProduct</legend>
@Html.EditorFor(m => m)
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
<div>
@Html.ActionLink("Back to List", "Index")
</div>
@section Scripts {
@Scripts.Render("~/bundles/jqueryval")
}
虽然我在开始时给出了一个简短的解释,但我应该解释一下这实际上是做什么的。您的原始HTML将产生如下输出:
<input name="ID" type="text" value="1" />
<input name="Name" type="text" value="Name 1" />
<input name="ID" type="text" value="2" />
<input name="Name" type="text" value="Name 2" />
如您所见,多个输入字段共享相同的名称。这就是模型绑定器绊倒的原因,因为您的操作告诉它绑定到集合,并且绑定器需要能够区分集合中的每个元素。 EditorTemplates
非常聪明,可以确定您何时使用集合并自动将索引应用于输入字段。上面的代码将生成这样的输出:
<input name="[0].ID" type="text" value="1" />
<input name="[0].Name" type="text" value="Name 1" />
<input name="[1].ID" type="text" value="2" />
<input name="[1].Name" type="text" value="Name 2" />
如您所见,这些字段现在有一个与之关联的索引。这为模型绑定器提供了将所有项目添加到集合所需的所有信息。现在,我们可以回过头来修复您的产品保存代码。
Gert所说的对于你试图保存productList
的方式仍然是正确的。您需要在该集合中的每个项目上设置EntityState.Modified
标志:
if (ModelState.IsValid)
{
foreach (var product in productList)
_db.Entry(product).State = EntityState.Modified;
_db.SaveChanges();
return RedirectToAction("Index");
}
看看是否有效。