如何使用Entity Framework Code First Fluent API在连接表上进行级联删除

8

我想使用Entity Framework Code First Fluent API来映射一个已有的数据库。我在处理多对多关系和级联删除时遇到了一些困难。

这是我的两个POCO类:

public class Foo
{
  public int FooId;
  public string FooDescription;
  public IList<Bar> Bars;
}
public class Bar
{
  public int BarId;
  public string BarDescription;
}

目前,我只有从FooBar的导航属性。这是因为在我的实际场景中,从BarFoo没有任何意义。从FooBar的关系实际上是0,n,这意味着一个Foo实例不需要Bar实例。

我的现有数据库在这两个表之间有一个联接表。这个表看起来像这样:

create table Foo_Bar(
FooId int not null,
BarId int not null
);

因为我不想在我的项目中创建这样的Poco类,所以我将表映射如下:
modelBuilder.Entity<Foo>()
  .HasMany(t => t.Bars)
  .WithMany()
  .Map(m =>
  {
    m.ToTable("Foo_Bar");
    m.MapLeftKey("FooId");
    m.MapRightKey("BarId");
  });

这对插入操作非常完美。在Foo上进行实体插入,然后在现有的Foo_Bar表上进行插入。

但是当我删除一个Foo时,我想删除连接表上的记录,但不删除子表中的记录。也就是说,我想删除Foo_Bar上的记录,但不扩展到Bar

这样的映射是否可能?我应该提到,在我的上下文中,我删除了OneToManyCascadeDeleteConventionManyToManyCascadeDeleteConvention,因为99%的时间我不会使用级联删除。这是一个特定的情况。

通过跟踪Entity Framework生成的SQL,我还注意到,如果我检索一个Foo实例,并将其内部的所有Bars设置为EntityState.Deleted,EF将删除Foo_BarBar中的两个记录。我还应该提到的最后一件事是,我正在使用断开连接的场景。

我试图搜索这个问题,但似乎我没有使用正确的关键词,因为我找不到任何处于这种情况的人。此时,我唯一看到的解决方案是映射一个FooBar类,但我希望有一种方法可以做到这一点。

--编辑

起初,我的删除方法是错误的。当我尝试删除时,我没有传递Foo实例及其填充的Bar列表。更改后,Entity Framwork为列表中的每个Bar创建一个删除语句。我仍然在更新方面遇到问题,例如同时添加和删除项目。将进行更多测试并在此处发布。

提前感谢您的帮助。

1个回答

7
但是当我删除一个 Foo 时,我希望删除“连接表”上的记录,但不要删除子表上的记录。也就是说,我想在 Foo_Bar 表中删除记录,而不扩展到 Bar 表。
这样的映射是否可能?
实际上,这是唯一可能的映射方式。你不能将级联删除扩展到 Bar 表,因为在关系(从数据库模式角度)中,Foo_Bar 连接表是关系中的“依赖方”,而 Bar 是“主要方”。通常,只有在删除“主要方”时,级联删除才会生效,而删除“依赖方”时则不会生效。
所以,当你删除 Foo 时,无需担心 Bar 会被删除,也不需要应用任何特殊设置。
但是,如果你已经移除了 ManyToManyCascadeDeleteConvention,那么你就会遇到问题。它会全局影响整个模型,并且没有选项来重新启用特定的多对多关系上的级联删除。 (与一对多或一对一关系中的 WillCascadeOnDelete(true) 不同,多对多关系中没有该选项。)
你确定在你的模型中真的需要移除 ManyToManyCascadeDeleteConvention 吗?我从未遇到过一个模型,在其中不删除连接实体(Foo 或 Bar)之一的相关条目是有意义的。

我同意你的观点。这是唯一可能的映射方式。我不记得当时为什么要这样指定了。至于需要删除约定的原因,我有一个场景,其中多对多关系的一端是与业务逻辑相关联的记录表。用户无法编辑其记录。即使有人错误地尝试删除记录,也不会破坏该端上的级联删除。但我想这确实是一个例外情况。现在我将启用约定并以其他方式处理异常情况。谢谢。 - Rodrigo Lira

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