c# - 为什么 Entity Framework 不在此处更新子对象?

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

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

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

    public string Name { get; set; }

    public virtual Address MailingAddress { get; set; }
}

我有一个表单,可以回发一个 View 模型,其中包含一个 customer 以及邮寄地址。这是 Controller 中的保存方法:

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);

然后更新。

这里的 child 还算是一个超脱的实体吗?我假设因为它是我正在获取的 Customer 的属性,所以它会自动与父级一起保存。为什么这适用于新记录而不适用于更新?

关于创建新记录的注意事项 - 创建了一个 Customer 记录,并有一个指向该地址的 MailingAddress_IdAddress 记录也被创建,但它的 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 安装它,配置您的模型以满足 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快速浏览wallktrough。它有示例代码,因此您可以下载并检查它。我是此 API 的所有者,我随时准备回答您的问题。

关于c# - 为什么 Entity Framework 不在此处更新子对象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/40542416/

相关文章:

c# - 使用 Expression 动态构建 Where 子句的 Entity Framework

c# - 通过简单注入(inject)器拦截/装饰 Entity Framework 的 DbContext.SaveChanges()

c# - 不存在具有键 'IEnumerable' 的 'x' 类型的 ViewData 项。'

c# - 转换 bool 值?在 View 中 bool

c# - 成员 'MySql.Data.MySqlClient.MySqlException,MySql.Data, Version=6.8.8.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d' 的类型未解析

c# - 尝试使用 UrlHelper 测试 Controller

c# - 我在 ASP.net core MVC 和 Azure iot hub 中遇到错误

c# - 如何使用 Json.NET.Schema 要求属性?

c# - LINQ:不同元素的 Sum()

c# - IFormFile IEnumerable 的大小为 1,即使指定了多个文件上传