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"
解决方案:
- 手动创建表,并将TPH表中的不同的子类型记录转移到不同的子类表中。
- 通过自编程序载入对象,进行持久化,然后清空所有表的数据,创建表,载入数据并通过EF Core插入。
由于数据量比较大,而且还有继承关系,手动去操作还是麻烦了一些,可以使用SQL查询进行简化;而第二个方案将由EF Core帮我们将数据插入到正确的位置。
方案1
准备临时数据库
将原来的数据库结构复制一份,并设置为开发环境。接下来修改数据库结构,TPH迁移到TPT模式,只需要在每一个子类表上使用[Table("")]
标记就行了(当然也可以使用FluentAPI)。标记好了之后,使用EF Migration:
add-migration migrateTPT
由于是只有结构的空表,直接操作就可以成功了。
迁移数据到临时数据库
将旧有数据传输到新的数据表中,尤其注意TPH与TPT之间表的在处理继承关系时的不同。
以AttachDeviceInfo为abstract类,AD_Insect_Info作为其中的一个子类
更新之后TPH表中的大量字段转移到了子类表中,因此可以使用数据库同步工具进行数据同步,忽略多余的字段就可以了。对于的TPT生成的子类表,通过Id字段与抽象类表进行匹配连接,因此需要手动插入对应类别的数据。
INSERT into "AD_Insect_Infos"
SELECT "Id",FALSE from "AttachDeviceInfos" WHERE "AttachDeviceTypeId" = 1
如果没有
AttachDeviceTypeId
字段,那么需要在TPH阶段先通过discriminator
将不同子类区分开,这个会麻烦一点。
转移回数据库
清空目标数据库(包括结构),并将临时数据库中的表同步到目标数据库中,手动调整_EFMigration表格的记录(指向最新版本),完成切换。
方案2
备份数据
在数据库还是原来结构的情况下,我们需要将现有的数据进行序列化,之前我写过一篇序列化文章,使用的是PROTOBUF序列化。这里由于传输的数据结构比较简单,可以使用System.Text.Json类库Json序列化到文件。
对于有继承关系的表的序列化,.NET 7的System.Text.Json新增了对应的支持,可以参考文档的相关实现。
准备临时数据库
将原来的数据库结构复制一份,并设置为开发环境。接下来修改数据库结构,TPH迁移到TPT模式,只需要在每一个子类表上使用[Table("")]
标记就行了(当然也可以使用FluentAPI)。标记好了之后,使用EF Migration:
add-migration migrateTPT
由于是只有结构的空表,直接操作就可以成功了。
迁移数据到临时数据库
由于临时数据库结构已经和既有数据库不同,无法通过程序直接连接两个数据库进行数据导入的操作,因此需要将数据反序列化到的新的数据库。
转移回数据库
清空目标数据库(包括结构),并将临时数据库中的表同步到目标数据库中,手动调整_EFMigration表格的记录(指向最新版本),完成切换。
总结
迁移到TPT时,可以使用临时数据库中转,将数据库的数据以新的结构存储下来,然后再同步到新数据库。当然也可以直接在正式数据库中操作:直接持久化,清空数据,然后再还原数据。当然这么风险更高,强调一点,在生产的数据库中进行操作需要格外谨慎,务必做好备份。
可以发现,在数据库中使用外键约束时,虽然给基于导航属性的应用(例如OData)提供了便利,同时将数据完整性检查后置到了数据库中;但是进行架构调整是一件比较麻烦的工作,对分布式应用也非常不友好。
P.S. TPT的查询性能很差,因此绝大多数场景都不推荐,仅在自己完全清楚并权衡了利弊的情况下再使用TPT。
EF Core从TPH迁移到TPT的更多相关文章
- 谈谈EF Core实现数据库迁移
作为程序员,在日常开发中,记忆犹新的莫过于写代码,升级程序.升级程序包含两部分:一是,对服务程序更新:二是,对数据库结构更新.本篇博文主要介绍数据库结构更新,在对数据库升级时,不知道园友们是否有如下经 ...
- EF Core Model更新迁移
EF Core 迁移 感觉就是以前EF Code First的自动同步数据库功能 内容:在你新增.更新TableModel后,如何自动化的更新DB中的真实Table.以及对这些更改进行一个版本控制. ...
- EntityFramework Core 2.x (ef core) 在迁移中自动生成数据库表和列说明
在项目开发中有没有用过拼音首字母做列名或者接手这样的项目? 看见xmspsqb(项目审批申请表)这种表名时是否有一种无法抑制的想肛了取名的老兄的冲动? 更坑爹的是这种数据库没有文档(或者文档老旧不堪早 ...
- .NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记
2.4.6 EF Core -- 更新 状态 自动变更检测 不查询删除和更新 并发 状态 Entity State Property State Entity State Added 添加 Uncha ...
- .Net Core建站(2):EF Core+CodeFirst数据库迁移
上一篇的话,说了下怎么使用EF7 实现 CodeFirst去生成数据库, 其实还有好多问题的,这次一点一点的解决吧,都挺简单,不过零零散散的,, 1.读取配置文件,获得链接字符串 2.使用数据库进行增 ...
- EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)
前言 终于踏出第一步探索EF Core原理和本质,过程虽然比较漫长且枯燥乏味还得反复论证,其中滋味自知,EF Core的强大想必不用我再过多废话,有时候我们是否思考过背后到底做了些什么,到底怎么实现的 ...
- Cookies 初识 Dotnetspider EF 6.x、EF Core实现dynamic动态查询和EF Core注入多个上下文实例池你知道有什么问题? EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)
Cookies 1.创建HttpCookies Cookie=new HttpCookies("CookieName");2.添加内容Cookie.Values.Add(&qu ...
- EF core (code first) 通过自动迁移实现多租户数据分离 :按Schema分离数据
前言 本文是多租户系列文章的附加操作文章,如果想查看系列中的其他文章请查看下列文章 主线文章 Asp.net core下利用EF core实现从数据实现多租户(1) Asp.net core下利用EF ...
- Entity Framework Core 之数据库迁移
前言 最近打算用.NET Core写一份开源的简易CMS系统,来练练手 所以又去深入研究了一下Entity Framework Core 发现其实有些细节园子里还是很少讲到. 特意整理了几个细节. 正 ...
- 一个官翻教程集合:ASP.NET Core 和 EF Core 系列教程
通过一个大学课程案例讲解了复杂实体的创建过程及讲解 1.ASP.NET Core 和 Entity Framework Core 系列教程——入门 (1 / 10) 2.ASP.NET Core 和 ...
随机推荐
- Tesstwo9.1.0配置步骤
一,配置步骤 环境:Tesstwo9.1.0+Android10(华为)+Android11(模拟器) 1.查看tess-two的最新版本(GitHub - rmtheis/tess-two: For ...
- 文献阅读——Watch Sat and LTO for CaDiCaL and Kissat
Norbert Mantheynmanthey@conp-solutions.comDresden, Germany Proceedings of SAT Competition 2022: Solv ...
- EF OwnsOne 主键不自增
menu public class Menu { /// <summary> /// id /// </summary> [Key, DatabaseGeneratedAttr ...
- 源码安装RocketMQ4.x可视化控制台详细教程
下载源码 https://github.com/apache/rocketmq-externals 进入 roccketmq-console cd roccketmq-console 编译打包 mvn ...
- 用python写xml文件
def writeInfoToXml(filename, config_id, obj_name): from xml.dom.minidom import Document ''' eg: < ...
- 004Java的一些基本概念
004Java的一些基本概念 1.Java特性和优势 Java至少具有以下特性: 简单性(没有头文件.没有指针运算.也没有分配内存等操作) 面向对象(万物皆对象) 可移植性(一次编写,到处运行 Wri ...
- vue+iview 表格行选中修改背景色
<Table :columns="columns" :no-data-text="L('NoDatas')" border :data="lis ...
- cookies、session、token
cookies 当登录的时候这个set-cookies就是把cookies的数据保存到了客户端的application/cookies中 session 表示为会话,存放在服务端,当用户登录,服务器就 ...
- 多线程JUC练习
package com.aliyun.test.learn; import java.util.concurrent.*; import java.util.concurrent.locks.Reen ...
- Matlab:4维、单目标、约束、粒子群优化算法
% 主调用函数(求最大值) clc; clear; close all; % 初始化种群 N = 100; % 初始种群个数 D = 4; % 空间维数 iter = 50; % 迭代次数 x_lim ...