Entity Framework 核心、DELETE CASCADE 和 [必需]

Entity Framework Core, DELETE CASCADE, and [Required]

我 运行 遇到 Entity Framework 核心中的 DELETE CASCADE 问题,我似乎找不到好的解决方案。

这是我的模型的超级简化版本:

User {UserID, Name}
Recipe {RecipeID, UserID}
Ingredient {IngredientID, UserID}
RecipeIngredient {RecipeID, IngredientID} *RecipeIngredient is the many-to-many table.

Recipe 和 Ingredient 都将 UserID 标记为 [Required],而 RecipeIngredient 将 RecipeID 和 IngredientID 标记为 [Required]。

问题是 SQL 不会创建数据库,因为 RecipeIngredient ("Introducing FOREIGN KEY constraint... may cause cycles or multiple cascade paths") 有多个级联删除路径。

所以我被卡住了...我已经尝试过一些想法,但没有任何效果。

  1. 这里有设计方案吗?我想保留我的外键,因为强制执行它是有意义的,但如果有设计解决方案,我愿意接受。

  2. 我的下一个想法是删除所有指向用户的 FK - 我必须通过我的 C# 代码在 DELETE 期间强制执行参照完整性,并且我可以在 CREATE 期间强制执行数据输入使用[必需的]。问题是 - [Required] 创建了一个 FK,并添加了 "ON DELETE CASCADE," 这让我们又回到了多级联删除路径问题。我真的很想保留 [Required] 因为与 Razor 页面的巧妙集成、客户端验证和错误等

  3. 下一个想法,在 OnModelCreating(...) 中将级联行为设置为 SetNull:

    modelBuilder.Entity()。 有一个(我=> i.User) .WithMany(u => u.Ingredients) .OnDelete(DeleteBehavior.SetNull);

    modelBuilder.Entity() .HasOne(r => r.Source) .WithMany(s => s.Recipes) .OnDelete(DeleteBehavior.SetNull);

但这会引发异常,因为即使我的 属性 在 Ingredient 和 Recipe 中设置为 Nullable:

[Required(ErrorMessage = "User ID is required.")] 
public Nullable<int> UserID { get; set; }

...由于 [Required] 属性,EF 仍将其创建为 NOT NULL 数据库列。

解决这个问题的方法是什么?据我所知,我应该只删除用户的所有 FK,并尝试将其作为 CREATE 上的必填字段强制执行,但我看不到使用数据注释来做到这一点的方法,我想这样做将此逻辑保留在我的代码优先模型中。

我建议禁用级联删除,因为开发人员通常希望非常小心删除哪些数据,禁用后您可以更精细地控制删除方面的数据。

您可以在 OnModelCreation(DbModelBuilder modelBuilder) 覆盖中执行此操作,如下所示 Context.cs class:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  base.OnModelCreating(modelBuilder);

  modelBuilder.Conventions.Remove<OneToManyCascadeDeleteConvention>();
  modelBuilder.Conventions.Remove<ManyToManyCascadeDeleteConvention>();
}

在 EF Core 中,Conventions class 不可用,因此您需要遍历实体类型并限制删除以达到预期的效果:

protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
  base.OnModelCreating(modelBuilder);

  foreach (var relationship in builder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys()))
  {
        relationship.DeleteBehavior = DeleteBehavior.Restrict;
  }
}