引入外键约束可能会导致循环或多个级联路径 - 为什么?
引入外键约束可能会导致循环或多个级联路径 - 为什么?
我已经苦思冥想了一段时间,但仍然无法弄清楚发生了什么。我有一个包含Side(通常为2个)的Card实体 - Card和Side都有一个Stage。我正在使用EF Codefirst迁移,但迁移失败并显示以下错误:\n引入FOREIGN KEY约束\'FK_dbo.Sides_dbo.Cards_CardId\'到\'table \'Sides\'可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。\n这是我的Card实体:\n
public class Card { public Card() { Sides = new Collection(); Stage = Stage.ONE; } [Key] [Required] public virtual int CardId { get; set; } [Required] public virtual Stage Stage { get; set; } [Required] [ForeignKey("CardId")] public virtual ICollection Sides { get; set; } }
\n这是我的Side实体:\n
public class Side { public Side() { Stage = Stage.ONE; } [Key] [Required] public virtual int SideId { get; set; } [Required] public virtual Stage Stage { get; set; } [Required] public int CardId { get; set; } [ForeignKey("CardId")] public virtual Card Card { get; set; } }
\n这是我的Stage实体:\n
public class Stage { // Zero public static readonly Stage ONE = new Stage(new TimeSpan(0, 0, 0), "ONE"); // Ten seconds public static readonly Stage TWO = new Stage(new TimeSpan(0, 0, 10), "TWO"); public static IEnumerableValues { get { yield return ONE; yield return TWO; } } public int StageId { get; set; } private readonly TimeSpan span; public string Title { get; set; } Stage(TimeSpan span, string title) { this.span = span; this.Title = title; } public TimeSpan Span { get { return span; } } }
\n奇怪的是,如果我在我的Stage类中添加以下内容:\n
public int? SideId { get; set; } [ForeignKey("SideId")] public virtual Side Side { get; set; }
\n迁移将成功运行。如果我打开SSMS并查看表,我可以看到Stage_StageId
已添加到Cards
(如预期/所需),但Sides
不包含对Stage
的引用(不符合预期)。\n如果我接下来在我的Side类中添加:\n
[Required] [ForeignKey("StageId")] public virtual Stage Stage { get; set; } public int StageId { get; set; }
\n我会在我的Side
表中看到StageId
列被添加。\n这是有效的,但现在在我的应用程序中,任何对Stage
的引用都包含一个SideId
,在某些情况下完全无关紧要。如果可能的话,我希望只给我的Card
和Side
实体一个基于上面的Stage类的Stage
属性,而不会污染stage类与引用属性...我做错了什么?
在EF Core中,当引入外键约束时,可能会出现循环或多个级联路径的问题。解决这个问题的方法是通过在OnModelCreating方法中修改DeleteBehavior属性,将级联删除行为设置为Restrict。
在代码中,可以通过以下方式实现将所有关系的级联删除行为设置为Restrict:
protected override void OnModelCreating(ModelBuilder modelBuilder) { foreach (var relationship in modelBuilder.Model.GetEntityTypes().SelectMany(e => e.GetForeignKeys())) { relationship.DeleteBehavior = DeleteBehavior.Restrict; } ..... 其他代码..... }
这将关闭所有关系的级联删除功能。对于某些用例,级联删除可能是一个期望的功能。
另一种解决方法是在关系配置中使用OnDelete方法将级联删除行为设置为Restrict,例如:
builder.HasOne(x => x.Stage).WithMany().HasForeignKey(x => x.StageId).OnDelete(DeleteBehavior.Restrict);
在这个例子中,builder是一个IEntityTypeConfiguration
总之,通过修改级联删除行为,可以解决Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths的问题。这样可以避免出现循环或多个级联路径的情况,从而确保数据库的完整性和一致性。
当在数据库中使用外键约束时,有可能会出现“Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths”这个错误。这个错误的出现原因是由于存在循环关系或多个级联路径。
解决这个问题的方法是将外键设置为可为空。如果外键不可为空,则需要删除相关对象,而循环关系不允许这样做。因此,需要将外键设置为可为空。
在代码中,可以使用int?
来表示可为空的外键,而不是使用int
来表示不可为空的外键。此外,还需要将外键属性上的[Required]标签移除,以确保外键可为空。
尝试关闭级联删除并不能解决这个问题,而将外键设为可为空则可以解决这个问题。
需要注意的是,如果不希望允许将外键设置为null(在原问题中,Stage是一个必填字段),则不应该使用这种解决方法。
当在使用外键约束时出现“Introducing FOREIGN KEY constraint may cause cycles or multiple cascade paths”错误时,可以通过将外键设置为可为空来解决这个问题。
引入外键约束可能会导致循环或多重级联路径-为什么会出现这个问题?
这个问题的出现是因为Stage
是必需的,与Stage
相关的所有一对多关系默认都启用了级联删除。这意味着,如果你删除一个Stage
实体:
- 删除将直接级联到
Side
- 删除将直接级联到
Card
,因为Card
和Side
之间有一个必需的一对多关系,并且默认情况下启用了级联删除,所以从Card
到Side
将再次级联
所以,从Stage
到Side
有两条级联删除路径,这导致了异常的出现。
你必须要么使Stage
在至少一个实体中是可选的(即从Stage
属性中删除[Required]
属性),要么使用Fluent API禁用级联删除(无法通过数据注释实现):
modelBuilder.Entity<Card>() .HasRequired(c => c.Stage) .WithMany() .WillCascadeOnDelete(false); modelBuilder.Entity<Side>() .HasRequired(s => s.Stage) .WithMany() .WillCascadeOnDelete(false);
谢谢Slauma。如果我像你上面演示的那样使用Fluent API,其他字段是否保留其级联删除行为?例如,我仍然希望在删除卡片时删除边。
: 是的,它只会影响从Stage
开始的关系。其他关系保持不变。
有没有办法知道哪些属性导致了错误?我遇到了相同的问题,查看我的类,我看不到循环在哪里。
这是他们实现的限制吗?对我来说,Stage
的删除直接级联到Side
和通过Card
级联看起来是没问题的。
假设我们将CascadeOnDelete设置为false。然后我们删除了与其中一条Card记录相关联的Stage记录。Card.Stage(FK)会发生什么?它保持不变吗?还是设置为Null?
如果您希望实现这种级联行为怎么办?在层次结构实体中,预期会在层次结构中和引用实体被删除时进行级联。
在EF Core 5中使用.OnDelete(DeleteBehavior.NoAction)
。
我不明白你的回答-我理解为Stage和Side有一个1对1的关系,而不是1对多。
此外,最新的EF Core不再具有WillCascadeOnDelete()。我认为你现在需要使用OnDelete()。