【问题标题】:EF Core 2.0 Trouble 'Cascading' Inserts for Related Entities When Updating Principle Entity更新主体实体时,EF Core 2.0 遇到相关实体的“级联”插入问题
【发布时间】:2018-12-05 09:05:42
【问题描述】:

使用 REST API 的 ASP.NET Core 2 Web 应用程序。目前使用 sqlite3 开发数据库。 (还尝试迁移到 SQL Server 并获得与以下相同的结果)。

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

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

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

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(没有所有其他属性)看起来像这样(我尝试了不同的变体,将外键“DataObjectId”从 json 中删除,设置为 null,或者设置为零以防万一干扰。):

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

在控制器操作方法中调试时,从请求正文创建的现有“DashboardItem”主体实体在添加到 DbContext 之前填充了引用属性“DataObject”,但不会在数据库中创建新的 DataObject。只有 DashboardItem 发出了 SQL 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.

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

【问题讨论】:

    标签: asp.net-core entity-framework-core asp.net-core-2.0 ef-core-2.0


    【解决方案1】:

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

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

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

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

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


    总结一下我现在学到的东西:

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

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

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

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

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

           _context.Update(entity) 
    

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

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

    【讨论】:

      猜你喜欢
      • 2022-12-09
      • 1970-01-01
      • 2020-09-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多