为什么Entity Framework不在这里更新子对象?

时间:2016-11-11 06:36:30

标签: c# asp.net-mvc entity-framework

我的Customer模型带有MailingAddress属性 - 这是Address类的导航属性:

public class Customer
{
    public int Id { get; set; }

    public string Name { get; set; }

    public virtual Address MailingAddress { get; set; }
}

我有一个发布视图模型的表单,其中包含customer以及邮寄地址。这是控制器中的save方法:

public ActionResult Save(CustomerFormViewModel viewModel)
{
    if (!ModelState.IsValid)
    {
        return View("CustomerForm", viewModel);
    }

    if (viewModel.Customer.Id == 0)
    {
        _context.Customers.Add(viewModel.Customer);
    }
    else
    {
        var customerInDb = _context.Customers
            .Single(c => c.Id == viewModel.Customer.Id);

        _context.Entry(customerInDb).CurrentValues.SetValues(viewModel.Customer);
    }

    _context.SaveChanges();

    return RedirectToAction("Index", "Customers");
}

发布新客户时,一切正常(大多数情况下,请参阅下面的说明),并创建客户以及相应的地址记录。但是,当我编辑现有条目时,客户会更新,但地址不会更新。我验证了更新的地址正在客户对象中传递。如果我添加这样一行:

_context.Entry(customerInDb.MailingAddress).CurrentValues.SetValues(viewModel.Customer.MailingAddress);

然后更新。

这里的孩子仍被视为独立实体吗?我假设因为它是Customer的一个属性我正在获取它将自动与父节点一起保存。为什么这会使用新记录而不是更新?

关于新记录创建的一个注意事项 - 创建了Customer记录,并且MailingAddress_Id指向该地址。还创建了Address记录,但其Customer_Id为空...想法为什么EF不在关系的那一侧添加密钥?地址模型和视图模型代码,以防它有用:

public class Address
{

    public int Id { get; set; }

    public string Street1 { get; set; }

    // Snip a bunch of address data properties     

    public virtual Customer Customer { get; set; }

}

public class CustomerFormViewModel
{
    // Snip irrelevant properties
    public Customer Customer { get; set; }

}

1 个答案:

答案 0 :(得分:3)

首先,如果您的CustomerAddress处于一对一关系中,则不需要外键。实际上,在一对一关系中,关系依赖方面的主键也是方的外键。其次,当您创建新Customer时,您使用context.Customers.Add(viewModel.Customer);并添加模型及其所有子模型,但是当您尝试使用_context.Entry(customerInDb).CurrentValues.SetValues(viewModel.Customer);进行更新时,它不会添加所有子导航属性,要这样做,你必须明确告诉EntityFramework:

var customerInDb = _context.Customers
            .Single(c => c.Id == viewModel.Customer.Id);
_context.Entry(customerInDb)
    .CurrentValues
    .SetValues(viewModel.Customer);

var mailingAddressInDb = _context.Addresses
            .Single(m => m.Id = viewModel.Customer.MailingAddress.Id);
_context.Entry(mailingAddressInDb)
    .CurrentValues
    .SetValues(viewModel.Customer.MailingAddress);

它应该适合你。但它有点尴尬。当你有几十个模型时,你甚至都不想想象它。

好消息

好消息是,有一个API可以从根本上解决这个问题。只需几步即可解决您的问题。您可以使用Install-Package Ma.EntityFramework.GraphManager从NuGet安装它,配置模型以满足prerequisites(这非常简单)并使用单行代码处理整个图形:

public ActionResult Save(CustomerFormViewModel viewModel)
{
    if (!ModelState.IsValid)
    {
        return View("CustomerForm", viewModel);
    }

    // Define state of whole graph with single line
    _context.AddOrUpdate(viewModel.Customer);
    _context.SaveChanges();

    return RedirectToAction("Index", "Customers");
}

请快速浏览一下CodeProject article。它有示例代码,因此您可以下载并检查它。我是此API的所有者,我随时准备回答您的问题。