Entity Framework Code First: 循环引用或多个级联路径

6
我有一个Booking类,其中包含一个预订联系人(Person)和一组导航属性(People),这些导航属性通过连接表链接到另一组导航属性(Bookings)中的Person。如何生成启用了预订联系人关系的级联删除的Booking表?当我在流畅API代码中将其省略时(默认设置为启用级联删除),我会从迁移中得到以下错误消息:
引入外键约束“FK_dbo.BookingPeople_dbo.People_PersonID”到表“BookingPeople”上可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。
无法创建约束或索引。请参阅前面的错误。
 modelBuilder.Entity<Person>()
   .HasMany<Booking>(s => s.aBookings)
   .WithRequired(s => s.Contact)
   .HasForeignKey(s => s.ContactId); 


 modelBuilder.Entity<Booking>()
   .HasMany(t => t.People)
   .WithMany(t => t.Bookings)
   .Map(m => {
     m.ToTable("BookingPeople");
     m.MapLeftKey("BookingID");
     m.MapRightKey("PersonID");
   });
1个回答

25

问题在于您有多条级联删除路径,这可能会导致尝试删除DB中的BookingPeople表中同一行。

您可以通过使用Fluent API中的一对多关系来禁用级联删除,以避免此类模棱两可的删除路径:

    modelBuilder.Entity<Booking>()
                .HasRequired(s => s.Contact)
                .WithMany(s => s.aBookings)
                .HasForeignKey(s => s.ContactId)
                .WillCascadeOnDelete(false);

或者通过将关系定义为可选的(使用可为空的外键),但是您无法使用Fluent Api配置级联删除来配置关系。

     modelBuilder.Entity<Booking>()
            .HasOptional(s => s.Contact)
            .WithMany(s => s.aBookings)
            .HasForeignKey(s => s.ContactId);// ContactId is a nullable FK property

此外,您可以使用以下方式取消级联删除约定:

modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();

或者在多对多关系的情况下:

modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();

如果您需要在删除一个Person时删除与之关联的所有Bookings,我的建议是将一对多关系配置为可选,并重写SaveChanges方法:

public override int SaveChanges()
{
    Bookings.Local
            .Where(r => r.ContactId == null)
            .ToList()
            .ForEach(r => Bookings.Remove(r));

    return base.SaveChanges();
 }

如果从属实体上的外键可以为空,Code First 不会在关系上设置级联删除,当主体被删除时,该外键将被设置为 null。这样,您就可以在 SaveChanges 方法中找到孤儿数据并删除它们。


我不确定我是否完全理解我的建议,因为我是新手,但是是否可能创建单向一对多关系,以便无需aBookings导航属性,这样是否能够解决问题或者删除路径仍然会混淆? - James Peters
即使您有一个单向的一对多关系,也无法解决问题,仍然会有多个路径可能最终尝试删除相同的行。因此,我的建议是将一对多关系定义为可选项,如果需要删除孤儿,则覆盖SaveChanges方法。我认为这个链接会帮助您更好地理解这个解决方案:使用Entity Framework删除孤儿 - ocuenca
谢谢您,@octavioccl。 这是一个非常全面的解释,有效地涵盖了我能从其他主题中找到的所有选项。 很多关于此主题的主题都涉及如何禁用级联删除,但没有涉及如何解决该问题,如果您确实需要该行为。 我本希望避免为每个关系编写手动删除方法,但从SQL Server的角度考虑,这也是有道理的为什么存在这些限制。 - Jeremy Caney
这个答案在M2M关系中没有意义。如果你删除一个Booking并级联删除相关的BookingPeople,即使存在从People到相同连接表的路径,它也不应该阻止你这样做。 - Riz

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