【问题标题】:EF Core - Removing a related entity from collection navigation property does not update databaseEF Core - 从集合导航属性中删除相关实体不会更新数据库
【发布时间】:2019-08-02 05:58:35
【问题描述】:

我正在努力理解这句话:

默认情况下,对于必需的关系,配置了级联删除行为,并且将从数据库中删除子/依赖实体。

在此 Microsoft EF Core 文档的“删除关系”段落中找到:https://docs.microsoft.com/en-us/ef/core/saving/related-data#removing-relationships

在.Net Core API上,博客和帖子的模型如下

public class Blog
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int BlogId { get; set; }

    public virtual ICollection<Post> Posts { get; set; }
}

public class Post
{
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public int PostId { get; set; }

    public int BlogId { get; set; }
    public virtual Blog Blog { get; set; }
}

当我通过 id 检索博客时

[HttpGet("{id}")]
public async Task<IActionResult> GetBlog([FromRoute] int id)
{
    return await context.Blog.Where(b => b.BlogId == id)
                             .Include(b => b.Posts).FirstOrDefaultAsync();
}

我以 JSON 格式接收

{
  "BlogId": 1,
  "Posts": [
    {
      "PostId": 1
    },
    {
      "PostId": 2
    }
  ]
}

当我将更新后的以下 JSON 发送回 API 以保留在数据库中时

{
  "BlogId": 1,
  "Posts": [
    {
      "PostId": 2
    }
  ]
}

[HttpPut("{id}")]
public async Task<IActionResult> PutBlog([FromRoute] int id, [FromBody] Blog blog)
{
    this.context.Update(blog);
    await this.context.SaveChangesAsync();

    return this.Ok();
}

一切正常,但没有修改任何条目。

由于帖子与博客之间的关系是必需的,并且相关帖子已从博客 collection-of-posts 属性中删除,不应将 PostId = 1 的帖子从数据库中删除吗?

真正让我困惑的是,只要我在博客和帖子之间使用多对多关系,而不更改逻辑中的任何其他内容,表 Blog_Post 中 PostId = 1 的条目实际上就被删除了。

【问题讨论】:

  • 这个问题有可能是它的根本原因吗? See GitHub issue Here 修复已合并到 release/2.2 分支,我正在使用 EFCore 2.2.0

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


【解决方案1】:

Update 仅关联实体并将所有属性标记为已修改。 AFAIK 它不会级联,也不会影响子集合。

作为一般规则,您应该避免将实体传入和传出 Web 客户端。如果您接受一个实体到 Web 客户端,那么找到将修改后的实体传递给服务器的调用、应用断点并在恢复之前使用调试器修改内容是一件简单的事情。如果服务器只是将实体附加到上下文并保存更改,我可以以应用程序不允许的方式修改数据。它还涉及向/从客户端发送比通常需要的更多信息。

关于删除孤儿的行为,这适用于您修改集合时。您可以在我对this 问题的回复中找到检测要添加和删除的实体并应用这些更改的示例。

【讨论】:

  • 感谢您的输入,但是,根据我的经验和文档,Update 执行“导航属性的递归搜索”,因此包括子集合 link 我理解代码在我原来的帖子中可能看起来不是很安全,但它不是生产代码,它只是测试 EF Core 文档在多大程度上按预期工作或表现。我会看看你的其他帖子
  • 我在您的链接中看到的问题的巨大区别是,如果您将“名称”添加到博客并将“标题”和“内容”添加到帖子,实体就会正确更新。实体被很好地识别,并且这些属性的值可以通过这个逻辑进行更新。我没有收到任何异常或错误消息
  • 对于由 DbContext 构造的实体(仍然是代理)可能就是这种情况。当您从控制器参数中的视图接收到实体时,它只是一个 POCO 对象。任何可能已记录的有关相关实体等的跟踪都将丢失。我不相信 Update 可以在没有首先从数据库中加载“真实”实体进行比较的情况下评估发生了什么变化。链接示例只是为了演示针对已加载实体的添加/删除关联行为。
  • 是的,它可以通过检查PK值来跟踪它们:“如果一个可达实体设置了它的主键值,那么它将在Modified状态下被跟踪。如果没有设置主键值,那么它将在已添加状态下进行跟踪。”
【解决方案2】:

实体框架将相关实体从主实体删除到从属实体。在您的案例中,原则是博客,而帖子是依赖实体。通过删除博客相关的帖子也应该被删除。 您还需要使用外键属性进行装饰,在这种情况下您的类将如下所示。

public class Post
{
    public int PostId { get; set; }
    public string Title { get; set; }
    public string Content { get; set; }
    public int BlogForeignKey { get; set; }
    [ForeignKey("BlogForeignKey")]
    public Blog Blog { get; set; }
}

你还需要定义删除行为,你可以通过 Fluent API 设置来做到这一点,Cascade 会删除相关实体,如果你不想删除相关实体然后使用 ClientSetNull ,它会

model.Entity<Post>().HasOne(p => p.Blog).WithMany(b => b.Posts)
    .HasForeignKey(p => p. BlogForeignKey)
    .OnDelete(DeleteBehavior.Cascade);

【讨论】:

  • 我明白你的意思。当文档说“您可以通过从集合导航中删除相关实体来删除关系”和“默认情况下,对于必需的关系,配置了级联删除行为”时,我应该如何理解文档,如果我无法从集合并且必须自己配置删除行为?此外,我尝试使用 Fluent API 设置级联删除,此时结果并没有改变
  • 是的,默认配置了级联删除行为,当您删除父对象时,它会删除子对象,但请记住您的父对象必须已填充子对象,如果子对象为空则不会删除。
猜你喜欢
  • 1970-01-01
  • 2021-11-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-09
  • 1970-01-01
  • 2020-09-04
  • 2020-07-04
相关资源
最近更新 更多