【问题标题】:How to properly control cascading deletion in EF Core using Fluent API?如何使用 Fluent API 正确控制 EF Core 中的级联删除?
【发布时间】:2019-11-28 20:57:39
【问题描述】:

我有两个实体,TagMember。一个成员可以标记多个标签。一个标签可用于标记多个成员。这是多对多关系的一个明显例子,因为我使用的是 EF Core,所以我必须声明一个显式连接器,我称之为 Tag_Member。我是这样配置的。

private void OnModelCreating(EntityTypeBuilder<Tag_Member> entity)
{
  entity.HasKey(e => new { e.TagId, e.MemberId });
  entity.HasOne(e => e.Tag);
  entity.HasOne(e => e.Member)
    .WithMany(e => e.Tag_Member)
    .HasForeignKey(e => e.MemberId);
}

删除时我希望强制执行的行为如下。

  • 删除 Tag_Member 的实例时,没有任何改变。
  • 删除 Tag 的实例时,所有连接的 Tag_Member 实例都会被删除。
  • 删除 Member 的实例时,所有连接的 Tag_Member 实例都会被删除。

我对两点感到困惑。当我如下所示添加删除条件时,我有很多选项可供选择,尽管阅读了智能感知,但我不确定使用哪个来强制执行上述行为。

entity.HasOne(e => e.Member)
  .WithMany(e => e.Tag_Member)
  .HasForeignKey(e => e.MemberId)
  .OnDelete(DeleteBehavior.NoAction);

我应该使用 NoActionClientNoActionRestrict 还是其他?我什至不清楚删除行为会影响哪些实体。是哪个?

混淆的第二点是我没有让 OnDelete() 出现在标签配置中。我没有使用 WithMany() 因为该实体缺少对互连实体的引用。我还能管理它的删除行为吗?我是否需要显式声明它以实现请求的行为?

entity.HasOne(e => e.Tag)
  .OnDelete(DeleteBehavior.NoAction);

这些类大致是这样的。

public class Tag { public Guid Id { get; set; } }
public class Member { public Guid Id { get; set; } }

public class Tag_Member
{
  public Guid TagId { get; set; }
  public Guid MemberId { get; set; }
  public Tag Tag { get; set; }
  public Member Member { get; set; }
}

我的参考主要是thisthis

编辑:根据答案中的建议,这是成员和标签之间关系的最终版本。

private static void OnModelCreating(EntityTypeBuilder<Member> entity)
{
  entity.HasKey(e => e.Id); ...
}

private static void OnModelCreating(EntityTypeBuilder<Tag> entity)
{
  entity.HasKey(e => e.Id); ...
}

private static void OnModelCreating(EntityTypeBuilder<Member_Tag> entity)
{
  entity.HasKey(e => new { e.MemberId, e.TagId });
  entity.HasOne(e => e.Member).WithMany().OnDelete(DeleteBehavior.NoAction);
  entity.HasOne(e => e.Tag).WithMany().OnDelete(DeleteBehavior.NoAction);
}

【问题讨论】:

    标签: c# entity-framework entity-framework-core ef-core-2.0


    【解决方案1】:

    我什至不清楚删除行为会影响哪些实体。是哪个?

    这很容易。级联删除总是影响 依赖 实体(即包含 FK 的实体)。

    我不确定使用哪个来强制执行上述行为。我应该使用NoActionClientNoActionRestrict 还是其他?

    您似乎正在使用 EF Core 3.0 预览版,它添加了更多尚未记录的选项。但是在数据库级别实现的classic级联删除的选项一直是Cascade

    我没有使用WithMany(),因为该实体缺少对互连实体的引用。

    为了能够流畅地配置关系方面,您必须使用Has + With 对来完全指定关系方。由于导航属性对于关系的任何一方都不是强制性的,因此您只需要将正确的参数传递给Has / With 方法 - 如果您确实有导航属性,请传递名称或lambda 表达式访问器,否则不传递任何内容(但仍包括调用)。例如

    entity.HasOne(e => e.Tag)
        .WithMany() // <--
        .OnDelete(DeleteBehavior.Cascade); // now you can do this
    

    但请注意,DeleteBehavior.Cascade必需 关系的默认(换句话说,当 FK 是不可为空的类型时),因此您通常不需要流畅的配置。如果属性名称遵循 EF Core 命名约定,则根本不需要流畅的配置。

    简单的例子可以看here

    【讨论】:

    • 我在一对多的情况下得到了这个想法,但在多对多的情况下,我不知何故无法理解它。我想级联删除,但只有一种方式,而且只有中途。含义 - 如果我删除一个标签,我希望所有标签成员实例都消失。但我希望关联的成员留下来。此外,如果我删除一个标签成员实例,我希望它不要级联任何东西。所以我想从标签到标签成员而不是成员本身进行级联删除。而且我不想将任何东西从标签成员级联到标签。 (对应的当然是从成员到标签。)我应该声明级联三次(对于标签,成员,标签成员)?
    • 应该声明3次级联(标签,成员,标签成员)?有人告诉我只在一个地方配置 X 和 Y 之间的关系(配置 X 或配置 Y)。但或许那个人是误会了。你怎么看?
    • 查看答案的第一部分。 Cascade 总是从委托人到从属人工作。多对多关系是简单的2一对多关系,TagMember是principal,join实体依赖于这两个关系。所以 cascade 完全按照您想要的方式工作 - 删除 TagMember 会删除相关链接,但不会删除链接中的“其他”对象。
    • 每个关系包含2个实体,但仍然是1个关系,所以必须配置一次。 a.HasMany(a =&gt; a.Bs).WithOne(b =&gt; b.A)b.HasOne(b =&gt; b.A).WithMany(a =&gt; a.Bs) 完全相同,都代表同一个关系。所以不,你只需要 2 个流利的配置,因为你有 2 个关系。
    • 对不起,太密集了。我想我开始掌握它,感谢您的解释。你会看一下我展示实体最终设置的编辑吗?我将两个关系的配置移到了管理中间人的方法中。这是最有意义的,并且看起来很干净。我还在两端将级联设置为 NoAction,因为删除中间实例永远不会影响链接的实例。
    猜你喜欢
    • 1970-01-01
    • 2014-09-26
    • 1970-01-01
    • 2018-07-19
    • 2016-03-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多