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") 有多个级联删除路径。
所以我被卡住了...我已经尝试过一些想法,但没有任何效果。
这里有设计方案吗?我想保留我的外键,因为强制执行它是有意义的,但如果有设计解决方案,我愿意接受。
我的下一个想法是删除所有指向用户的 FK - 我必须通过我的 C# 代码在 DELETE 期间强制执行参照完整性,并且我可以在 CREATE 期间强制执行数据输入使用[必需的]。问题是 - [Required] 创建了一个 FK,并添加了 "ON DELETE CASCADE," 这让我们又回到了多级联删除路径问题。我真的很想保留 [Required] 因为与 Razor 页面的巧妙集成、客户端验证和错误等
下一个想法,在 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;
}
}
我 运行 遇到 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") 有多个级联删除路径。
所以我被卡住了...我已经尝试过一些想法,但没有任何效果。
这里有设计方案吗?我想保留我的外键,因为强制执行它是有意义的,但如果有设计解决方案,我愿意接受。
我的下一个想法是删除所有指向用户的 FK - 我必须通过我的 C# 代码在 DELETE 期间强制执行参照完整性,并且我可以在 CREATE 期间强制执行数据输入使用[必需的]。问题是 - [Required] 创建了一个 FK,并添加了 "ON DELETE CASCADE," 这让我们又回到了多级联删除路径问题。我真的很想保留 [Required] 因为与 Razor 页面的巧妙集成、客户端验证和错误等
下一个想法,在 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;
}
}