I’m stumped with a problem in ASP.NET MVC 3 – handling editing and updating of a master-detail relationship from a single MVC view.
I’m using Entity Framework 4.2 code-first to build a small app. It uses a Person class (which has Name, FirstName and so on), and an Address class (with Street, Zipcode, City etc.).
These two classes have a m:n relationship to one another, modelled in EF code-first with:
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; }
}
}
I filled the database manually with some data and retrieval of the data using EF 4.2 works just fine.
I have an ASP.NET MVC PersonController, in which I am loading a Person including all its current addresses, and show it using a Razor view. I’d like to allow the user to change or update existing addresses, as well as delete or insert addresses. The controller gets the data for that person from a WCF service like this:
public ActionResult Edit(int id)
{
Person person = _service.GetPerson(id);
return View(person);
}
and the person’s addresses are filled properly when retrieving.
The view for this works fine – it shows all the data from the Person instance as expected.
@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>
}
However, when I’m trying to handle the POST upon saving the person being edited, I cannot seem to get access to the addresses that were displayed (and possibly edited) in the view:
[HttpPost]
public ActionResult Edit(Person person)
{
if (ModelState.IsValid)
{
_service.UpdatePerson(person);
return RedirectToAction("Index");
}
return View(person);
}
The person.Addresses property is always null in this case. Hmmm…. what am I missing?? How can I edit a Person-to-Addresses relationship on an ASP.NET MVC view, and get back the Person AND its associated Addresses from my view, so I can pass them to EF to save them back to the database tables??
I suspect that the input fields for the address have wrong names.
I would suggest you using editor templates. So replace your
@foreachloop in the Razor view with a single call of theEditorForhelper for the Addresses property:and now define an editor template for the address which will automatically be rendered for each element of the collection (
~/Views/Shared/EditorTemplates/Address.cshtml):Now when you submit, the
Addressesproperty on your Person model will be correctly bound.Remark: the editor template could also be placed inside
~/Views/XXX/EditorTemplates/Address.cshtmlwhere XXX is the current controller. In this case it can be reused only within the views of the current controller instead of making it globally visible (by putting it in Shared).For more in-depth overview of how the wire format of the default model binder you may take a look at the following article.