【发布时间】:2014-12-29 22:17:26
【问题描述】:
我正在尝试在我的数据模型中实现异构关联(Entity Framework 6,代码优先方法)。
我有一个现有的类结构,我们称它们为Tree、Branch 和Leaf。一个Tree 可能有很多Branch 对象,而一个Branch 可能有很多Leaf 对象。三个级别之间的关系有一个cascade-delete 行为(删除一个分支,你也删除了叶子等)。
现在我试图让用户在每个级别上添加一个类似评论的对象。 I had a few problems related to data-modelling,因为我希望 3 种实体类型中的每一种都能够拥有许多 cmets,并且每个评论都属于一个且仅属于一个条目。我还希望所有 cmets 都在同一张桌子上。我尝试了两种不同的方法:
替代。 1
实现继承,以便Comment(抽象)可以是TreeComment、BranchComment 或LeafComment,遵循Table per Hierarchy (TPH) 方法(如图所示,对于例如,here) 为 cmets 提供一个抽象类 (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。如果我尝试将其更改为多个表,我会得到:
引入 FOREIGN KEY 约束“FK_Comment_Branch_BranchID” 表“注释”可能会导致循环或多个级联路径。指定开 DELETE NO ACTION 或 ON UPDATE NO ACTION,或修改其他 FOREIGN KEY 约束。
我知道这是因为 SQL Server“不知道”在任何时候都应该使用 Comment 表中的 FK 之一。
替代。 2
使用Table per Type (TPT) 方法将Tree/Branch/Leaf 三重组概括为CommentableEntity,并将Comment 表连接到该抽象表。这可以通过在模型类中实现继承来实现(就像我之前所做的那样)并向每个子类添加注释 [Table("Tree")]、[Table("Branch")] 和 [Table("Leaf")] 以确保我们为每个子类(而不是TPH 中的单个表)。模型,然后看起来像这样:
这种方法有两个问题:
删除具体对象(例如分支)will not delete the base entryin the abstract table,留下“垃圾”(抽象实体及其 cmets)。
抽象类和具体类之间的 FK 关系缺少
cascade delete。所以我不能真正删除基础对象。如果我尝试添加一个,我会收到另一个抱怨,即引入此类规则会导致 多个级联路径的循环。
我也尝试在这两种方法上使用 DB 触发器 (CREATE TRIGGER ... INSTEAD OF DELETE...),但它们似乎是一个很大的禁忌,因为 EF 无法跟踪它们所做的更改。
这令人沮丧,我确信这(树结构上的 cmets)是 Web 开发中非常典型的场景;但我似乎找不到允许它的方法。我正在寻找关于如何有效地对这些关系建模(EF 6 Code First)而不给业务逻辑层施加过多权重的所有建议。
编辑:
我相信这就是用户@Deepak Sharma 在他的评论中提到的:TPH 节点类中的继承。如果是这样,出于同样的原因,这也不起作用:多个级联路径的循环。
【问题讨论】:
-
当 cmets 和节点之间没有级联删除时,您将在“Alt 1”中遇到什么问题。这对我来说似乎很合理。
-
删除节点不会删除 cmets,也不会将它们的 FK 设置为节点为
NULL(尝试添加SET NULL约束也会给我 可能导致循环或多个级联路径 错误)。如果你不能做任何这些事情,你就不能删除一个节点(因为它会违反 FK 约束)。 -
如果我说你只需要两张桌子怎么办??一个用于树/分支/叶子,另一个仅用于评论??我们也可以这样做。如果你说我已经为你提供了代码..
-
您是否正在考虑树/分支/叶上的 Table Per Hierarchy(而不是 Table Per Type)?即,那些节点的单个表(例如
Node)包括一个 Discriminator 属性,其值可以是“树/分支/叶子”?这有点混乱,因为不同的节点类具有一些不同的属性,更不用说它会导致自引用表可能与其他两种方法具有相同的问题。我想我已经尝试过这种方法,但我会再试一次并发布结果。 -
Alt 1 上的消息不是反过来吗?我读到“删除评论不应删除节点”,这是合理的。您不能通过将 Comment DependentNavigationPropertyConfigaration 的 WillCascadeOnDelete 属性设置为 false 来在 DbModelBuilder 中使用“Comment”完成删除周期。
标签: c# sql-server asp.net-mvc entity-framework data-modeling