簡體   English   中英

從單個路徑刪除時刪除級聯

[英]Remove cascade on delete from a single path

跟蹤我的數據模型。 我刪除了所有不重要的注釋,以使其簡潔明了。

public class Bubble
{
    public int Id { get; set; }
    public string Name { get; set; }
    public ICollection<Level> Levels { get; set; }
}

public class Level
{
    public int Id { get; set; }
    public int BubbleId { get; set; }
    [ForeignKey("ParentLevel")]
    public int? LevelId { get; set; }

    public string Name { get; set; }

    public Level ParentLevel { get; set; }
    public Bubble Bubble { get; set; }

    public ICollection<Level> Levels { get; set; }
    public ICollection<Item> Items { get; set; }
}

public class Item
{
    public int Id { get; set; }
    public int LevelId { get; set; }

    public string Name { get; set; }

    public Level Level { get; set; }
}

如果我這樣創建它,我將得到錯誤:

在表“ Item”上引入FOREIGN KEY約束“ FK_dbo.Item_dbo.Level_LevelId”可能會導致循環或多個級聯路徑。 指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY約束。 無法創建約束或索引。 查看先前的錯誤。

如果我添加此代碼,則數據庫創建有效:

modelBuilder.Entity<Item>()
    .HasRequired(i => i.Level)
    .WithMany(l => l.Items)
    .HasForeignKey(i => i.LevelId)
    .WillCascadeOnDelete(false);

但是當我刪除氣泡時,我得到了這個錯誤:

SqlException:DELETE語句與REFERENCE約束“ FK_dbo.Item_dbo.Level_LevelId”沖突。 數據庫“ MvBubbles1”的表“ dbo.Item”的列“ LevelId”中發生了沖突。 該語句已終止。

所以我相信問題是,級別是自引用的。 因為我有很多一對多的關系,並且刪除和層疊操作在除Level和Item之間的所有地方都可以使用,所以唯一的區別是在可行的情況下父級不是自引用的。 我相信我只需要刪除一個級聯路徑,但此刻我不知道如何執行此操作,而在不禁用刪除功能的情況下,問題出在哪里,但我不想禁用它。

刪除代碼:

db.Bubbles.Remove(bubble);
db.SaveChanges();

級聯關閉時無法刪除氣泡的原因是,您仍然具有要刪除氣泡的外鍵級別。

此外,假設您要刪除氣泡2。氣泡2具有最高級別20。
氣泡3的級別為21,是級別20的子級別。
氣泡4具有級別22,它是級別21的子級別。

如果刪除氣泡2,是否應刪除氣泡3和4的水平?

假設您的軟件沒有這些特性:沒有循環的關卡引用,並且所有關卡都來自同一Bubble。

您可以在刪除“關卡”和“氣泡”之前使要刪除的“氣泡”的所有關卡的所有LevelIds無效:

// we want to remove Bubble 2
var levelsToRemove = dbContext.Levels.Where(level => level.BubbleId == 2).ToList();
// nullify all levelIds:
foreach (var levelToRemove in levelsToRemove)
{
    levelToRemove.LevelId = null;
}
// TODO: maybe we need an extra SaveChanges

// Remove the Levels and the Bubble:
dbContext.Levels.RemoveRange(levelsToRemove);

var bubbleToRemove = dbContext.Find(2); // TODO: exception if not found
dbContext.Bubbles.Remove(bubbleToRemove);
dbContext.SaveChanges();

適當的解決方案

您的代碼必須防止循環引用和帶有Levels的Bubbles屬於其他Bubbles的Levels子級別,這應該使您知道您的數據庫不夠規范。

考慮給您的LevelCollection。 這將是一對零或一對的關系,或者如果所有Bubble 2的所有關卡都有若干共同點,請考慮將這些東西放到LevelCollection中,並讓您的Bubble擁有零個或多個LevelCollection(一對多)。 )。

每個LevelCollection都完全屬於一個Bubble。 LevelCollection具有零個或多個級別。

現在可以保證,如果LevelCollection 42屬於Bubble 2,則LevelCollection 42的所有級別都屬於Bubble2。您可以從級別中刪除外鍵BubbleId。

這不會阻止循環的關卡引用,但會阻止一棵樹的關卡屬於不同的氣泡

為什么必須關閉級聯

通常,如果您與一所學校有一對多關系,並且有許多學生,則在刪除學校時,您還希望自動刪除其所有學生。 啟用層疊時,實體框架將首先刪除所有帶有外鍵的項目,這些外鍵指向您要移動的項目,然后再刪除您的項目。

氣泡和色階無法自動完成

讓我們添加一些氣泡和水平

                            Id | Name
Add Bubble with name A  =>  1  |  A

                                                  Id | BubbleId | LevelId
Add Level without Parent for Bubble 1         =>  10 |    1     |  null
Add sub Level of Level 10 Parent for Bubble 2 =>  11 |    1     |   10

Now give Level 10 a new LevelId               =>  10 |    1     |   11

好的,級聯打開,讓我們刪除Bubble 1。

在實體框架可以執行此操作之前,它必須刪除所有具有Bubble 1外鍵的內容。因此,我們需要先刪除10級,而不能刪除,因為我們必須先刪除11級。 但是,無法刪除第11級,因為無法刪除第10級,依此類推

我們已經制作了一個小圓圈,但是您可以想象一下,如果您擁有一個具有1000個色階的圓圈,將會發生什么。

您的代碼可能會阻止您創建一個圓,但是實體框架不能阻止您這樣做。

另一個問題:假設氣泡2的級別為20。級別20是級別21的子級別,而級別21具有到氣泡1的外鍵。如果刪除氣泡1,則氣泡2的級別會發生什么? 有人可能會說:它變成了泡泡2的最高等級,其他人可能會說:不,泡泡2失去了他的等級。 實體框架無法檢測到您想要的內容,因此您必須自己進行操作並關閉級聯。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM