开发者

Propogate Changes to Nested Entities in MVC 3

I have two POCO classes a User and an Address. Address is a complex object, and a User has one Address. I want to create a single User view that allows the create/edit of an Address on the same form via MVC Scaffolding + Entity Framework + Repository Pattern. The Create User View works correctly, but when I try to make changes to the Address in the User Edit View, the changes don't get propagated. How do I force the changes to propogated down to the Address object from the User View? Everything else is auto generated.

POCO Classes

public class TestDB : DbContext
{
    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {
    }

    public DbSet<Address> Addresses { get; set; }
    public DbSet<User> Users { get; set; }
}

public class Address
{
    public int ID { get; set; }
    public string Street1 { get; set; }
    public string Street2 { get; set; }
    public string City { get; set; }
    public string State { get; set; }
    public string PostalCode { get; set; }
    public string Country { get; set; }
}

public class User
{
    public int ID { get; set; }
    public string UserName { get; set; }

    /*[ForeignKey("Address")]
    public int AddressID { get; set; }*/
    public virtual Address Address { get; set; }
}

Edit User View

<% using (Html.BeginForm()) { %>
<%: Html.ValidationSummary(true) %>
<fieldset>
    <legend>User</legend>

    <%: Html.HiddenFor(model => model.ID) %>
        <%: Html.Partial("CreateOrEdit", Model) %>
    <legend>Address</legend>
    <div class="editor-label">
        <%: Html.LabelFor(model => model.Address.Street1) %>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.Address.Street1)%>
        <%: Html.ValidationMessageFor(model => model.Address.Street1)%>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.Address.Street2)%>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.Address.Street2)%>
        <%: Html.ValidationMessageFor(model => model.Address.Street2)%>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.Address.City)%>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.Address.City)%>
        <%: Html.ValidationMessageFor(model => model.Address.City)%>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.Address.State)%>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.Address.State)%>
        <%: Html.ValidationMessageFor(model => model.Address.State)%>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.Address.PostalCode)%>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.Address.PostalCode)%>
        <%: Html.ValidationMessageFor(model => model.Address.PostalCode)%>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.Address.Country)%>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.Address.Country)%>
        <%: Html.ValidationMessageFor(model => model.Address.Country)%>
    </div>
    <p>
        <input type="submit" value="Save" />
    </p>
</fieldset>
<% } %>

<div>
    <%: Html.ActionLink("Back to List", "Index") %>
</div>

Editor Template for Address:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<AddressTest.Models.Address>" %>

<script src="<%: Url.Content("~/Scripts/jquery-1.5.1.min.js") %>" type="text/javascript"></script>
<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()) { %>
<%: Html.ValidationSummary(true) %>
<fieldset>
    <legend>Address</legend>

    <%: Html.HiddenFor(model => model.ID) %>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.Street1) %>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.Street1) %>
        <%: Html.ValidationMessageFor(model => model.Street1) %>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.Street2) %>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.Street2) %>
        <%: Html.ValidationMessageFor(model => model.Street2) %>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.City) %>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.City) %>
        <%: Html.ValidationMessageFor(model => model.City) %>
    </div>

    <div c开发者_Python百科lass="editor-label">
        <%: Html.LabelFor(model => model.State) %>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.State) %>
        <%: Html.ValidationMessageFor(model => model.State) %>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.PostalCode) %>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.PostalCode) %>
        <%: Html.ValidationMessageFor(model => model.PostalCode) %>
    </div>

    <div class="editor-label">
        <%: Html.LabelFor(model => model.Country) %>
    </div>
    <div class="editor-field">
        <%: Html.EditorFor(model => model.Country) %>
        <%: Html.ValidationMessageFor(model => model.Country) %>
    </div>

    <p>
        <input type="submit" value="Save" />
    </p>
</fieldset>
<% } %>

<div>
<%: Html.ActionLink("Back to List", "Index") %>
</div>

Change to User Edit View:

<% using (Html.BeginForm()) { %>
<%: Html.ValidationSummary(true) %>
<fieldset>
    <legend>User</legend>

    <%: Html.HiddenFor(model => model.ID) %>
        <%: Html.Partial("CreateOrEdit", Model) %>
    <%: Html.Editor("Address", Model.Address) %>

    <p>
        <input type="submit" value="Save" />
    </p>
</fieldset>
<% } %>


The solution was to modify the User Update Repository Method and mark the entity state modified for the Address. I also needed to add a hidden property for the Address.ID on the User Edit View.

User Insert/Update Repository Method

    public void InsertOrUpdate(User user)
    {
        if (user.ID == default(int)) {
            // New entity
            context.Users.Add(user);
        } else {
            // Existing entity
            context.Entry(user.Address).State = EntityState.Modified; // Update Address
            context.Entry(user).State = EntityState.Modified;
        }
    }

Additional Hidden Property on User Edit View

<%: Html.HiddenFor(model => model.ID) %>
<%: Html.HiddenFor(model => model.Address.ID) %> <!-- Added this line! -->

Controller Edit Action

    // POST: /User/Edit/5

    [HttpPost]
    public ActionResult Edit(User user)
    {
        if (ModelState.IsValid) {
            userRepository.InsertOrUpdate(user);
            userRepository.Save();
            return RedirectToAction("Index");
        } else {
            ViewBag.PossibleAddresses = addressRepository.All;
            return View();
        }
    }


The trick here maybe to use editortemplates

Basically make a partial view in Views/Shared/EditorTemplates (create the folder if it doesn't exist) called Address and strongly typed to your address class. This template will contain your editor for the address (cut out of the above, and change model => model.Address to model=>model . Then take all of the address editors out of your view and put in EditorFor(model.Address) instead. I think this will generate slightly different fieldnames that will work.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜