簡體   English   中英

實體框架6代碼優先 - 關於樹結構的評論

[英]Entity Framework 6 Code First - Comments on a Tree Structure

我正在嘗試在我的數據模型( 實體框架6,代碼優先方法 )中實現異構關聯。

我有一個現有的類結構,讓我們稱它們為TreeBranchLeaf Tree可以有許多Branch對象,而Branch可以包含許多Leaf對象。 三個級別之間的關系具有cascade-delete行為(刪除分支,您還刪除葉子等)。

現在,我試圖讓用戶在每個級別上添加類似注釋的對象。 我有一些與數據建模有關的問題 ,因為我希望3種實體類型中的每一種都能夠有很多注釋,每條注釋都屬於一個且只有一個條目。 我也希望所有評論都在同一張表中。 我嘗試了兩種不同的方法:


Alt鍵。 1

實現繼承,以便Comment (abstract)可以是TreeCommentBranchCommentLeafComment ,遵循每個層次結構表 (TPH)方法(例如,如此處所示 )具有用於注釋的抽象類( Comment )然后派生它通過編寫如下模型來實現TreeCommentBranchComment等。

public abstract class Comment
{
    // ID
    [DatabaseGenerated(DatabaseGeneratedOption.Identity)]
    public Guid ID { get; set; }
}

public class TreeComment: Comment
{
    // Foreign Keys
    public Guid TreeID { get; set; }

    // Navigation Properties
    public virtual Tree Tree { get; set; }
}

(... BranchComment and LeafComment ...)

(... add virtual ICollection<TreeComment> to Tree, virtual ICollection<BranchComment> to Branch, etc.)

......可以用這個圖表表達:

每個評論層次結構的表

這種方法的問題是Comment表和其他3之間的關系沒有ON DELETE CASCADEON DELETE SET NULL設置。 如果我嘗試將其更改為多個表格,我會得到:

在表'Comment'上引入FOREIGN KEY約束'FK_Comment_Branch_BranchID'可能會導致循環或多個級聯路徑。 指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY約束。

我知道這是因為SQL Server“不知道”只應該在任何時候使用Comment表中的一個FK。


Alt鍵。 2

使用每種類型 (TPT)方法將Tree / Branch / Leaf三重奏概括為CommentableEntity ,並將Comment表連接到該抽象表。 這可以通過在模型類中實現繼承(就像我之前做的那樣)並添加注釋[Table("Tree")][Table("Branch")][Table("Leaf")]來實現。確保我們為每個子表獲取一個表(而不是像TPH中的單個表)。 模型,然后看起來像這樣:

樹元素上的每種類型的表

這種方法有兩個問題:

  1. 刪除具體對象(例如分支) 不會刪除 抽象表中 的基本條目 而是留下“垃圾”(抽象實體及其注釋)。

  2. 抽象類和具體類之間的FK關系缺少cascade delete 所以我無法真正刪除基礎對象。 如果我嘗試添加一個,我會得到另一個抱怨,如何引入這樣的規則將導致多個級聯路徑的循環


我也試過在這兩種方法上使用數據庫觸發器( CREATE TRIGGER ... INSTEAD OF DELETE... ),但它們似乎是一個很大的禁忌,因為EF無法跟蹤它們所做的更改。

這令人沮喪,我確信這(對樹結構的評論)是Web開發中非常典型的場景; 但我似乎找不到允許它的方法。 我正在尋找有關如何有效地建模這些關系(EF 6 Code First)的所有建議,而不會過多地重視業務邏輯層。

編輯:

我相信這是用戶@Deepak Sharma在他的評論中提到的:節點類中的TPH繼承。 如果是這樣,由於同樣的原因,這也不起作用: 多個級聯路徑的循環

每個樹元素的層次結構表

好的,所以這就是我目前正在解決的問題:

我選擇了第二種選擇 - 使用TPT方法將Tree / Branch / Leaf三重奏(讓我們將這些“節點”稱為簡化)概括為一個CommentableEntity (基類) - 如上所示。 我最終得到一個表用於三個節點類中的每一個+一個基類,它保存與Comment表的關系。

然后,在我的InitializeDatabase(MuDbContext context) ,我使用context.Database.ExecuteSqlCommand()方法為數據庫中的每個表添加了一個存儲過程觸發器

1)存儲過程必須在EF中映射,如下所示:

this.MapToStoredProcedures(s => s.Delete(d => d.HasName("TriggerName").Parameter(b => b.ID, "parameter_name")));

...對於三個模型中的每一個,基本上是默認刪除的替代品。 在我的例子中,我編寫它,以便首先刪除其表( Tree / Branch / Leaf )中的實際節點,然后刪除相應的基礎對象( CommentableEntity )。

2)刪除節點后觸發,並確保刪除相應的基礎對象。

如果您想知道為什么我有這樣的冗余(觸發器和存儲過程幾乎完全相同),那是因為無論何時刪除節點(例如樹),EF都會調用其存儲過程。 為了刪除它。 然后,通過DB的cascade-delete嵌套節點(樹的分支),它不刪除基礎對象,而不是通過存儲過程刪除。因此,觸發器。 另一方面,如果我只有觸發器(沒有存儲過程),EF會在刪除后嚇壞,因為它無法跟蹤其變化。

當然,我可以改變每個存儲過程。 對於每個表,以便它們也刪除所有嵌套對象並刪除cascade-delete設置。 但目前的解決方案對我來說似乎很有效。

如果我發現這實際上沒有用,我會測試一下並刪除這個答案。 如果您發現此方法有任何缺點(並知道如何避免它們),請發表評論。

可能的答案是通過在OnModelCreating方法中聲明一些規則來擴充Alt 1。 此外,這假設Tree,Branch和Leaf類具有針對它們的Comments集合。

在您的DbContext中,您可以執行以下操作...

public class YourDbContext : DbContext
{
     ... your DbSet properties

    protected override void OnModelCreating(DbModelBuilder modelBuilder)
    {

        modelBuilder.Entity<Tree>()
                .HasMany<TreeComment>(o => o.Comments)
                .WithRequired(com => com.Tree)
                .HasForeignKey(com => ds.TreeID)
                .WillCascadeOnDelete(false);
     }
}

暫無
暫無

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

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