如何使用Fluent API在EF Core中正确控制级联删除?

3

我有两个实体,标签成员。一个成员可以被多个标签标记。一个标签可以用来标记多个成员。这是一个明显的多对多关系,由于我使用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);
}

我希望实现的删除行为如下:
  • 删除的实例时,不做任何更改。
  • 删除的实例时,将删除任何连接的实例。
  • 删除的实例时,将删除任何连接的实例。

我有两个困惑。当我添加如下的删除条件时,我有很多选项可供选择,并且尽管阅读了智能感知,我仍然不确定应该使用哪个来实现上述行为。

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

我的参考资料主要是这个这个编辑:根据答案中的建议,这是成员和标签之间关系的最终版本。
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);
}
1个回答

4

我甚至不确定删除行为会影响哪些实体。它是哪个?

很简单。级联删除总是会影响到从属实体(即包含外键的实体)。

我不确定应该使用什么来确保上述行为。我应该使用NoActionClientNoActionRestrict还是其他什么东西?

你似乎正在使用 EF Core 3.0 预览版,该版本添加了尚未记录在文档中的更多选项。但是在数据库级别上实现的经典级联删除选项始终是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 命名约定,则根本不需要流畅的配置。点击这里可以查看一个简单的示例:https://www.entityframeworktutorial.net/efcore/configure-many-to-many-relationship-in-ef-core.aspx

1
我理解一对多的情况,但在多对多的情况下,我不知道该如何掌握。我想级联删除,但只有单向和到一半的程度。这意味着,如果我删除一个标签,我希望所有标签成员实例都被删除。但我希望关联的成员保留。此外,如果我删除一个标签成员实例,我希望它不会级联任何东西。所以我想从标签到标签成员进行级联删除,但不是到成员本身。而且,我不想从标签成员到标签级联任何内容(当然也是从成员到标签)。我应该声明三次级联删除(针对标签、成员、标签成员)吗? - DonkeyBanana
1
级联操作始终从主体到从属进行。多对多关系实际上是两个一对多关系,TagMember 是主体,而连接实体则依赖于这两个关系。因此,级联操作会按照您的期望方式正常工作——删除 TagMember 将同时删除相关链接,但不会删除链接中的“其他”对象。 - Ivan Stoev
1
每个关系包括2个实体,但仍然是1个关系,因此必须配置一次。a.HasMany(a => a.Bs).WithOne(b => b.A)b.HasOne(b => b.A).WithMany(a => a.Bs)完全相同,两者都代表同一个关系。因此,你只需要2个流畅的配置,因为你有2个关系。 - Ivan Stoev
1
抱歉我有点迟钝。多亏了你的解释,我开始理解了。你能否看一下我编辑的内容,其中我展示了实体的最终设置?我将两个关系的配置移动到管理中间人的方法中。这样做最合理且看起来很干净。我还在两端将级联设置为NoAction,因为删除中间实例不应影响链接的实例。 - DonkeyBanana
1
正如我在之前的评论中提到的,无论您使用哪种方法配置关系都没有关系,因为有2个“男人”,但只有1个(共享的)关系。 所以你所做的是不对的。(1)级联应该是Cascade ,因为您正在配置删除“主要男子”时会发生什么。(2)如果您在主要男人中具有集合导航属性,则.WithMany()将导致问题,就像最初的代码所示。 确保使用.WithMany(e => e.Tag_Member)而不是。 - Ivan Stoev
显示剩余4条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接