我在ASP.NET MVC 3中遇到了一个问题——从单个MVC视图处理主-细节关系的编辑和更新。
我首先使用实体框架4.2代码来构建一个小型应用程序。它使用Person
类(具有Name
、FirstName
等)和Address
类(具有Street
、Zipcode
、City
等)
这两个类之间存在m:n关系,首先在EF代码中使用建模
public class Person
{
private List<Address> _addresses;
// bunch of scalar properties like ID, name, firstname
public virtual List<Address> Addresses
{
get { return _addresses; }
set { _addresses = value; }
}
}
public class Address
{
private List<Person> _people;
// bunch of scalar properties like AddressID, street, zip, city
public virtual List<Person> People
{
get { return _people; }
set { _people = value; }
}
}
我用一些数据手动填充了数据库,使用EF 4.2检索数据效果很好。
我有一个ASP.NET MVC PersonController
,在其中我加载一个Person
,包括它的所有当前地址,并使用Razor视图显示它。我希望允许用户更改或更新现有地址,以及删除或插入地址。控制器从WCF服务中获取该人员的数据,如下所示:
public ActionResult Edit(int id)
{
Person person = _service.GetPerson(id);
return View(person);
}
并且在检索时该人的地址被正确填写。
此视图运行良好——它按预期显示了Person
实例中的所有数据。
@model DomainModel.Person
<h2>Edit</h2>
@using (Html.BeginForm()) {
@Html.ValidationSummary(true)
<fieldset>
<legend>Person</legend>
@Html.HiddenFor(model => model.PersonId)
<div class="editor-label">
@Html.LabelFor(model => model.Name)
</div>
<div class="editor-field">
@Html.EditorFor(model => model.Name)
@Html.ValidationMessageFor(model => model.Name)
</div>
.... and a few more of those scalar properties being edited....
<hr/>
<h4>Addresses</h4>
@foreach (var item in Model.Addresses)
{
<tr>
<td>
@Html.EditorFor(modelItem => item.Street)
</td>
<td>
@Html.EditorFor(modelItem => item.ZipCode)
</td>
<td>
@Html.EditorFor(modelItem => item.City)
</td>
</tr>
}
<hr/>
<p>
<input type="submit" value="Save" />
</p>
</fieldset>
}
然而,当我试图在保存被编辑的人时处理POST时,我似乎无法访问视图中显示(可能编辑)的地址:
[HttpPost]
public ActionResult Edit(Person person)
{
if (ModelState.IsValid)
{
_service.UpdatePerson(person);
return RedirectToAction("Index");
}
return View(person);
}
在这种情况下,person.Addresses
属性始终为null。嗯……我错过了什么??如何在ASP.NET MVC视图中编辑Person-to-Addresses关系,并从视图中获取Person
and及其关联的Addresses
,以便将它们传递给EF以将它们保存回数据库表??
我怀疑地址的输入字段有错误的名称。
我建议您使用编辑器模板。因此,将Razor视图中的@foreach
循环替换为Addresses属性的EditorFor
助手的单个调用:
<h4>Addresses</h4>
@Html.EditorFor(x => x.Addresses)
<hr/>
现在为地址定义一个编辑器模板,该模板将自动为集合的每个元素呈现(~/Views/Shared/EditorTemplates/Address.cshtml
):
@model Address
<tr>
<td>
@Html.EditorFor(x => x.Street)
</td>
<td>
@Html.EditorFor(x => x.ZipCode)
</td>
<td>
@Html.EditorFor(x => x.City)
</td>
</tr>
现在,当您提交时,Person模型上的Addresses
属性将被正确绑定。
备注:编辑器模板也可以放置在~/Views/XXX/EditorTemplates/Address.cshtml
中,其中XXX是当前控制器。在这种情况下,它只能在当前控制器的视图中重用,而不能使其全局可见(通过将其放在Shared中)。
有关默认模型绑定器的有线格式的更深入的概述,您可以查看下面的文章。
我相信通过将foreach块更改为for块,您将获得所需的效果,如下所示:
@for (var i = 0; i < Model.Addresses.Count(); i++)
{
<tr>
<td>
@Html.EditorFor(model => model.Addresses[i].Street)
</td>
<td>
@Html.EditorFor(model => model.Addresses[i].ZipCode)
</td>
<td>
@Html.EditorFor(model => model.Addresses[i].City)
</td>
</tr>
}
这当然要求Addresses集合可以通过索引访问。
原因是在foreach
块中,生成的html将类似于:
<input type="text" name="Street" value="Test Street 1" />
for
块将生成以下内容:
<input type="text" name="Addresses[0].Street" value="Test Street 1" />
模型绑定器然后使用表单字段名称将其与模型类的属性相匹配。
这两个例子都经过了简化,并不是100%正确。