多重继承的实体框架TPC



我使用EF与TPC和我有多重继承假设我有

员工(抽象)

Developer(继承自Employee)

SeniorDeveloper(继承自Developer)

我在数据库中插入了一些行,EF正确地读取了它们。

但当我插入一个新的SeniorDeveloper时,值被写入SeniorDeveloper AND Developer数据库表,因此仅查询Developers (context.Employees.OfType())也获得最近添加的SeniorDevelopers。

是否有一种方法告诉EF,它应该只存储在一个表中,或者为什么EF回落到TPT策略?

因为它看起来不像EF支持与TPC的多重继承,我们最终使用TPC的员工到开发人员和TPT之间的开发人员和高级开发人员…

我相信这是有原因的,尽管我可能没有看到全貌,可能只是猜测。

事实上,(在我看来)EF通过读取Developer表来在TPT场景中仅列出非高级开发人员(您的查询用例)的唯一方法是使用鉴别符,我们知道EF在TPT/TPC策略中不使用鉴别符。

为什么?那么,请记住,所有高级开发人员都是开发人员,所以他们有Developer记录和SeniorDeveloper记录是很自然的(也是必要的)。

唯一的例外是如果Developer是一个抽象类型,在这种情况下,您可以使用TPC策略来完全删除Developer表。然而,在您的情况下,Developer是具体的。

当前解决方案

记住这一点,并且在Developer表中没有标识符,确定任何开发人员是否是非高级开发人员的唯一方法是检查它是否为而不是高级开发人员;换句话说,通过验证SeniorDeveloper表或任何其他子类型表中没有开发人员的记录。

这听起来有点明显,但是现在我们明白了为什么当SeniorDeveloper表的基类型(Developer)是具体的(非抽象的)时必须使用和访问它。

当前实现

我是根据记忆写这篇文章的,所以我希望它不会太偏离,但这也是Slauma在另一条评论中提到的。您可能想要启动SQL分析器并验证这一点。

实现的方式是请求表投影的UNION。这些投影只是添加了一个以某种编码方式声明自己类型的标识符[1]。在联合集中,可以基于此鉴别符对行进行筛选。

[1]如果我没记错的话,它大概是这样的:0X代表基本类型,0X0X代表联合中的第一个子类型,0X1X代表第二个子类型,以此类推。

权衡# 1

我们已经可以识别出一种权衡:EF可以在表中存储一个鉴别符,也可以在"运行时"生成一个"。

  • 存储标识符的缺点是空间效率较低,并且可能"丑陋"(如果这是一个参数)。优点是查找性能,在非常特定的情况下(我们只需要的记录)类型)。
  • "运行时"标识符的缺点是,对于相同的用例,查找性能不那么好。优点是更节省空间。

乍一看,似乎有时我们更愿意为查询性能交换一点空间,而EF不允许我们这样做。

在现实中,并不总是清楚何时;通过请求两个表的UNION,我们只需查找两个索引而不是一个索引,性能差异可以忽略不计。对于单级继承,它不可能低于2x(因为所有子类型集都是不相交的)。但是等等,还有更多。

权衡# 2

请记住,我说过存储鉴别器方法的性能优势只会出现在查找基类型记录的特定用例中。为什么呢?

好吧,如果您正在搜索可能是也可能不是高级开发人员的开发人员[2],那么无论如何您都必须查找SeniorDeveloper。虽然这似乎是显而易见的,但可能不那么明显的是,EF无法提前知道类型是一种类型还是另一种类型。这意味着它必须发出两个在最坏的情况下:一个查询在Developer表上,如果结果集中有一个高级开发人员,第二个查询在SeniorDeveloper表上。

不幸的是,额外的往返可能比两个表的UNION对性能的影响更大。(我说的是可能,我还没有证实过。)更糟糕的是,对于结果集中有一行的每个子类型,它都会增加。假设一个类型有3、5、甚至10个子类型。

这就是你的取舍#2。

[2]请记住,这种操作可以来自应用程序的任何部分,而解决权衡必须全局完成,以满足所有进程/应用程序/用户。同时,EF团队必须为所有EF用户做出这些权衡(尽管他们确实可以为这些权衡添加一些配置)。

一个可能的替代

通过批处理SQL查询,可以避免多次往返。EF必须为条件查找(T-SQL)向服务器发送一些过程逻辑。但是,由于我们已经在权衡#1中确定,在许多情况下,性能优势很可能可以忽略不计,因此我不确定这是否值得付出努力。也许有人可以打开一个问题票来确定它是否有意义。

结论

在未来,也许有人可以用一些创造性的解决方案来优化这个特定场景中的一些典型操作,然后在优化涉及到这种权衡时提供一些配置开关。

但是现在,我认为EF选择了一个公平的解决方案。以一种奇怪的方式,它几乎更干净。

几点说明

  • 我相信在某些情况下使用联合是一种优化。在其他情况下,它将是一个外部连接,但对标识符(以及其他一切)的使用保持不变。

  • 你提到多重继承,这让我一开始有点困惑。在常见的面向对象术语中,多重继承是一个类型具有多个基类型的构造。许多面向对象类型系统不支持这一点,包括CTS(所有。net语言都使用)。你是另有意思吗?

  • 您还提到EF将"退回"到TPT策略。在Developer/SeniorDeveloper的情况下,TPC策略将与TPT策略具有相同的结果,因为Developer是具体的。如果你真的想要一个单表,那么你必须使用TPH策略。

最新更新