繁体   English   中英

实体框架级联删除 - FOREIGN KEY约束

[英]Entity Framework Cascade delete - FOREIGN KEY constraint

我有以下型号的问题:

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);

但我无法更新数据库。 我在Package Manager控制台中遇到以下错误:

在表'ProjectPages'上引入FOREIGN KEY约束'FK_dbo.ProjectPages_dbo.ProjectPages_FooterId'可能会导致循环或多个级联路径。 指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY约束。

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

如果有多个级联删除路径可能会导致尝试删除DB中的同一行,则会导致该异常。 想象一下,如果你的ProjectPage具有相同的HeaderFooter 当您尝试删除该ProjectPage ,由于您的关系配置,将有两个路径尝试删除DB中的同一行(一个用于Header ,另一个用于Footer )。

您可以通过使用Fluent API禁用两个关系之一中的级联删除,或者将某些关系定义为可选(使用可为空的外键,但无法使用级联删除配置关系)来避免此类不明确的删除路径。

更新

是的,你有两个FK作为选项,但两个关系都配置了级联删除,这就是EF抛出异常的原因。 我的建议只与级联删除设置了一个关系。 关于其他关系,我担心你必须手动完成。 例如,如果您选择手动删除Footer ,那么当您要删除ProjectPage ,您必须将Footer属性设置为null 这里的问题是你的DB中可能有孤儿。 为避免这种情况,您可以覆盖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();
}

另一种方法是使用default(Guid)值设置FooterId 由于您的PK属性( Id )的类型是Guid而且它不是Identity,您必须在将ProjectPage添加到DB之前设置该属性。 因此,将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方法。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM