EF Core从TPH迁移到TPT
Intro#
EF Core支持多种方式处理具有继承关系的表,现在支持TPH
、TPC
(EF Core 7)、TPT
,具体的实现方式可以参考官方文档和这篇文章。
大致总结一下不同的方式的区别:TPH:所有的类型都放在一张表中,使用discriminator字段用以区别不同的类型TPT:不同的子类型有单独的表存放子类独有的字段,父虚类型也有一张单独的表存放共有的字段。TPC:不为父虚类新建表,只有子类型有单独的表,并且表内有父类和子类所有的字段。
由于TPT
两张表的外键关联设计,在进行查询时,会自动进行的JOIN等连表查询操作,因此极限性能不太行。需要经常用查询父类的情况,TPH
就挺好;需要经常查询子类的时候,TPC
就非常适合。按照官方的说法,正常情况TPH
就已经满足大多数的场景(这也是EF Core的默认设置),性能也是数一数二的,如果遇到了需要经常单独查询子类型的问题,可以优先考虑TPC
,仅在一些特殊情况下应该考虑TPT
。哪些是特殊情况?
请查阅官网这篇文章的详细讨论以了解三种不同方式对EF Core生成SQL的影响。
可能适合的场景#
我遇到的这么一个场景,有以下特点:
- 子类非常多,并且不同的子类字段的区别也很大,使用TPH会使得这个表格的规格非常大,并且空字段非常多。
- 继承的层级很短,只有一层继承关系。
- 需要经常进行基于父类的查询,直接在一张表执行查询的效率要比在的TPC分布在不同表中查询的效率高。(注意,这里说的父类的查询是指直接使用Raw SQL的查询,使用EF Core在父类的查询会翻译成非常多的LEFT JOIN,导致性能低下。)
直接使用TPH
或者使用TPC
都不是非常满意,而TPT
提供了一张父类的表存储公共的字段的这种方法,就显得非常适合。
注:TPC不符合数据库范式设计原则,TPH在空字段非常多的情况下也非常不优雅,强迫症可以使用TPT。
迁移#
如果是空表的话,直接使用EF Migration就可以了,麻烦的已经有既有数据的情况,由于数据表引用的对象从的总表转移到了子类表,因此直接执行的数据库迁移会提示违反了外键约束。
23503: insert or update on table "AD_AnimalCamera_Data" violates foreign key constraint "FK_AD_AnimalCamera_Data_AD_AnimalCamera_Infos_AttachDeviceId"