为什么实体框架不在这里更新子对象?

本文关键字:对象 更新 在这里 实体 框架 为什么 | 更新日期: 2023-09-27 18:17:29

我有一个具有MailingAddress属性的Customer模型-这是指向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");
}

当发布一个新客户时,一切工作正常(大多数情况下,请参见下面的注释),并且该客户与相应的地址记录一起创建。但是,当我编辑一个现有条目时,客户更新了,但地址没有更新。我验证了更新后的地址正在customer对象中传递。如果我添加这样一行:

_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; }
}

为什么实体框架不在这里更新子对象?

首先,如果您的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安装它,配置您的模型以满足先决条件(这很容易),并使用一行代码处理整个图形:

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的文章以获得快速演示。它有示例代码,因此您可以下载并检查它。我是这个API的所有者,我准备回答你的问题。