EF Core 2.0 在更新主要实体时出现问题 'Cascading' 插入相关实体

EF Core 2.0 Trouble 'Cascading' Inserts for Related Entities When Updating Principle Entity

ASP.NET 使用 REST API 的核心 2 Web 应用程序。目前使用 sqlite3 开发数据库。 (还尝试迁移到 SQL 服务器并得到与下面相同的结果)。

我正在向 Web 客户端发送一个实体,客户端对该实体进行了更改,包括添加一个新的相关实体,然后更新后的主要实体作为 json 在 PUT 请求的正文中发回.

我希望新的相关实体会自动创建,但这并没有发生。主要实体的简单属性已正确更新,但引用属性未更新。我没有得到任何异常或任何东西——它似乎只是忽略了引用属性。

简化类(我删除了不应该影响关系的其他属性):

public partial class DashboardItem {
    public int Id { get; set; }
    public int? DataObjectId { get; set; }
    public DataObject DataObject { get; set; }

}

public partial class DataObject {
    public int Id { get; set; }
}

关联 属性 的 DbContext Fluent API 部分:

        modelBuilder.Entity<DashboardItem>(entity => {
            entity.HasOne(p => p.DataObject)
            .WithMany()
            .HasForeignKey(p => p.DataObjectId);
        });

PUT 的控制器方法:

    [HttpPut("{id}")]
    public async Task<IActionResult> PutDashboardItem([FromRoute] int id, [FromBody] DashboardItem entity)
    {
        if (!ModelState.IsValid)
        {
            return BadRequest(ModelState);
        }

        if (id != entity.Id)
        {
            return BadRequest();
        }

        _context.Entry(entity).State = EntityState.Modified;

        try{
            await _context.SaveChangesAsync();
        }catch (DbUpdateConcurrencyException)
        {
            if (!DashboardItemExists(id)){
                return NotFound();
            }else {
                throw;
            }
        }
        return NoContent();
    }

简化的 json(没有所有其他属性)看起来像这样(我尝试了从 json 中删除外键 "DataObjectId" 的不同变体,设置为空,或设置为零以防可能产生干扰。):

{
  Id:1,
  DataObjectId:null,
  DataObject:{
    Id: 0
  }
}

在控制器操作方法中调试时,从请求主体创建的现有 "DashboardItem" 原则实体在添加到 DbContext 之前填充了引用 属性 "DataObject",但是new DataObject 永远不会在数据库中创建。只有 SQL 为 DashboardItem 发出了 UPDATE 语句,没有为 DataObject 发出 INSERT。

我还尝试使用 DbContext.SaveChanges() 而不是 .SaveChangesAsync() 使控制器方法同步而不是异步,因为 used to be a problem 与早期版本的 EF Core 相关创建相关实体,即使我使用的是 2.0,它已经对此进行了修复。同样的结果。

This EFCore Doc sounds like it should just work out of the box.

这在之前的项目中对我有用。我在这里错过了什么?

基本上,我的错误是假设更新数据的过程比在 Web 应用程序中从客户端发送更新数据时实际要简单得多。

深入挖掘后,我的控制器方法中用于处理 PUT 请求的以下行似乎是问题所在:

_context.Entry(entity).State = EntityState.Modified;

以这种方式将实体条目状态设置为已修改会导致 Entity Framework 核心忽略相关对象的引用属性 - SQL 生成的更新将仅处理实体中的列 table。

This 简单的总结最终让我走上了正确的道路。


总结我现在学到的东西:

此控制器方法正在处理一个 'detached' 实体,该实体已被编辑并从客户端发回。 DbContext 尚未跟踪此实体,因为我通过每个 http 请求获得了上下文的新实例(因此该实体被视为 'detached')。因为还没有被跟踪,当它被添加到DbContext中时,需要告诉context这个实体是否被改变了,以及如何对待它。

有几种方法可以告诉 DbContext 如何处理分离的实体。其中:

(1) 将实体状态设置为 EntityState.Modified 将导致所有属性都包含在 SQL 更新中(无论它们是否实际更改),除了参考属性相关实体:

      _context.Entry(entity).State = EntityState.Modified;

(2) 通过调用 DbContext.Update 添加实体将执行与上述相同的操作,但将包括引用属性,还包括更新中这些实体的所有属性,无论它们是否已更改与否:

       _context.Update(entity) 

方法 #2 让事情对我有用,我只是试图让新的相关子实体在其父实体的更新中创建。

除此之外,DbContext.Attach() 和 DbContext.TrackGraph 听起来像是您提供了更细粒度的控制来指定要包含在更新中的特定属性或相关实体。