实体框架级联删除 - FOREIGN KEY 约束
Posted
技术标签:
【中文标题】实体框架级联删除 - FOREIGN KEY 约束【英文标题】:Entity Framework Cascade delete - FOREIGN KEY constraint 【发布时间】:2015-04-29 03:53:46 【问题描述】:我对以下型号有疑问:
public class ProjectPage
[Key]
public Guid Id get; set;
public Guid? HeaderId get; set;
public ProjectPage Header get; set;
public Guid? FooterId get; set;
public ProjectPage Footer get; set;
在创建模型时我有这个:
modelBuilder.Entity<ProjectPage>().HasOptional(p => p.Header).WithMany().HasForeignKey(p => p.HeaderId).WillCascadeOnDelete(true);
modelBuilder.Entity<ProjectPage>().HasOptional(p => p.Footer).WithMany().HasForeignKey(p => p.FooterId).WillCascadeOnDelete(true);
但我无法更新数据库。我在包管理器控制台中遇到以下错误:
引入 FOREIGN KEY 约束 'FK_dbo.ProjectPages_dbo.ProjectPages_FooterId' 在桌子上 “ProjectPages”可能会导致循环或多个级联路径。指定开 DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。
有人可以解释如何删除项目页面(可以是另一个项目页面中的页脚或页眉)吗?
【问题讨论】:
【参考方案1】:当您有多个级联删除路径可能会结束尝试删除数据库中的同一行时,会导致该异常。想象一下,如果您有 ProjectPage
与相同的 Header
和 Footer
。当您尝试删除 ProjectPage
时,由于您的关系配置,将有两条路径尝试删除 DB 中的同一行(一条用于Header
,另一条用于Footer
)。
您可以通过使用Fluent API 禁用两个关系之一中的级联删除或将某些关系定义为可选(使用可为空的外键,但您不能使用级联删除配置关系)来避免此类不明确的删除路径)。
更新
是的,您将两个 FK 作为可选项,但两个关系都配置了级联删除,这就是 EF 抛出该异常的原因。我的建议是只设置与级联删除的一种关系。关于其他关系,恐怕你必须手动完成。例如,如果您选择手动删除Footer
,那么当您要删除ProjectPage
时,您必须将Footer
属性设置为null
。这里的问题是您的数据库中可能有孤儿。为避免这种情况,您可以覆盖 Context 上的 SaveChanges
以查找和删除孤儿:
public override int SaveChanges()
ProjectPages
.Local
.Where(r => r.Footer== null && r.FooterId!=default(Guid)).Select(r=>r.FooterId)
.ToList()
.ForEach(id => ProjectPages.Remove(ProjectPages.Find(id)));
return base.SaveChanges();
另一种方法是将FooterId
设置为default(Guid)
值。由于您的 PK 属性 (Id
) 的类型是 Guid
并且它不是身份,您必须在将 ProjectPage
添加到数据库之前设置该属性。因此,将FooterId
设置为default(Guid)
是另一种标记要删除的实体的方法。如果您选择此变体,您的 SaveChanges
方法可能如下所示:
public override int SaveChanges()
ProjectPages
.Local
.Where(r => r.Footer!= null && r.FooterId!=default(Guid)).Select(r=>r.Footer)
.ToList()
.ForEach(pp=> ProjectPages.Remove(pp));
return base.SaveChanges();
或者:
public override int SaveChanges()
ProjectPages
.Local
.Where(r => r.Footer!= null && r.FooterId!=default(Guid)).Select(r=>r.Footer)
.ToList()
.ForEach(pp=> Entry(pp).State=EntityState.Deleted);
return base.SaveChanges();
这样可以避免调用Find
方法。
【讨论】:
但是当我删除级联删除时,它告诉我,该行具有依赖关系并且无法删除...我有可选的外键。 所以在我从表中删除页脚或页眉之前,我需要自己在所有依赖项中创建空页眉或页脚? 你好@AndreyMykhaylov,我已经更新了我的答案,希望现在对你有所帮助。以上是关于实体框架级联删除 - FOREIGN KEY 约束的主要内容,如果未能解决你的问题,请参考以下文章
引入 FOREIGN KEY 约束可能会导致循环或多个级联路径 - 为啥?
可能会导致循环或多个级联路径。指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束
SQL Server 引入 FOREIGN KEY 约束可能导致循环或多个级联路径
错误:引入FOREIGN KEY约束可能会导致循环或多个级联路径
在表“ReservedSeats”上引入 FOREIGN KEY 约束“FK_ReservedSeats_Seats_SeatId”可能会导致循环或多个级联路径