【问题标题】:Entity Framework Cascade delete - FOREIGN KEY constraint实体框架级联删除 - FOREIGN KEY 约束
【发布时间】: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 约束。

有人可以解释如何删除项目页面(可以是另一个项目页面中的页脚或页眉)吗?

【问题讨论】:

    标签: c# entity-framework entity-framework-6


    【解决方案1】:

    当您有多个级联删除路径可能会结束尝试删除数据库中的同一行时,会导致该异常。想象一下,如果您有 ProjectPage 与相同的 HeaderFooter。当您尝试删除 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,我已经更新了我的答案,希望现在对你有所帮助。
    猜你喜欢
    • 2016-11-04
    • 1970-01-01
    • 2014-01-19
    • 1970-01-01
    • 2023-03-29
    • 1970-01-01
    • 2018-06-10
    • 2014-12-20
    • 1970-01-01
    相关资源
    最近更新 更多