簡體   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