[英]Entity Framework 6 Code First - Comments on a Tree Structure
我正在嘗試在我的數據模型( 實體框架6,代碼優先方法 )中實現異構關聯。
我有一個現有的類結構,讓我們稱它們為Tree
, Branch
和Leaf
。 Tree
可以有許多Branch
對象,而Branch
可以包含許多Leaf
對象。 三個級別之間的關系具有cascade-delete
行為(刪除分支,您還刪除葉子等)。
現在,我試圖讓用戶在每個級別上添加類似注釋的對象。 我有一些與數據建模有關的問題 ,因為我希望3種實體類型中的每一種都能夠有很多注釋,每條注釋都屬於一個且只有一個條目。 我也希望所有評論都在同一張表中。 我嘗試了兩種不同的方法:
實現繼承,以便Comment
(abstract)可以是TreeComment
, BranchComment
或LeafComment
,遵循每個層次結構表 (TPH)方法(例如,如此處所示 )具有用於注釋的抽象類( Comment
)然后派生它通過編寫如下模型來實現TreeComment
, BranchComment
等。
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 CASCADE
或ON DELETE SET NULL
設置。 如果我嘗試將其更改為多個表格,我會得到:
在表'Comment'上引入FOREIGN KEY約束'FK_Comment_Branch_BranchID'可能會導致循環或多個級聯路徑。 指定ON DELETE NO ACTION或ON UPDATE NO ACTION,或修改其他FOREIGN KEY約束。
我知道這是因為SQL Server“不知道”只應該在任何時候使用Comment表中的一個FK。
使用每種類型的表 (TPT)方法將Tree
/ Branch
/ Leaf
三重奏概括為CommentableEntity
,並將Comment
表連接到該抽象表。 這可以通過在模型類中實現繼承(就像我之前做的那樣)並添加注釋[Table("Tree")]
, [Table("Branch")]
和[Table("Leaf")]
來實現。確保我們為每個子表獲取一個表(而不是像TPH中的單個表)。 模型,然后看起來像這樣:
這種方法有兩個問題:
抽象類和具體類之間的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.