Intro

EF Core支持多种方式处理具有继承关系的表,现在支持TPHTPC(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"

解决方案:

  1. 手动创建表,并将TPH表中的不同的子类型记录转移到不同的子类表中。
  2. 通过自编程序载入对象,进行持久化,然后清空所有表的数据,创建表,载入数据并通过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的更多相关文章

  1. 谈谈EF Core实现数据库迁移

    作为程序员,在日常开发中,记忆犹新的莫过于写代码,升级程序.升级程序包含两部分:一是,对服务程序更新:二是,对数据库结构更新.本篇博文主要介绍数据库结构更新,在对数据库升级时,不知道园友们是否有如下经 ...

  2. EF Core Model更新迁移

    EF Core 迁移 感觉就是以前EF Code First的自动同步数据库功能 内容:在你新增.更新TableModel后,如何自动化的更新DB中的真实Table.以及对这些更改进行一个版本控制. ...

  3. EntityFramework Core 2.x (ef core) 在迁移中自动生成数据库表和列说明

    在项目开发中有没有用过拼音首字母做列名或者接手这样的项目? 看见xmspsqb(项目审批申请表)这种表名时是否有一种无法抑制的想肛了取名的老兄的冲动? 更坑爹的是这种数据库没有文档(或者文档老旧不堪早 ...

  4. .NET 云原生架构师训练营(模块二 基础巩固 EF Core 更新和迁移)--学习笔记

    2.4.6 EF Core -- 更新 状态 自动变更检测 不查询删除和更新 并发 状态 Entity State Property State Entity State Added 添加 Uncha ...

  5. .Net Core建站(2):EF Core+CodeFirst数据库迁移

    上一篇的话,说了下怎么使用EF7 实现 CodeFirst去生成数据库, 其实还有好多问题的,这次一点一点的解决吧,都挺简单,不过零零散散的,, 1.读取配置文件,获得链接字符串 2.使用数据库进行增 ...

  6. EntityFramework Core 运行dotnet ef命令迁移背后本质是什么?(EF Core迁移原理)

    前言 终于踏出第一步探索EF Core原理和本质,过程虽然比较漫长且枯燥乏味还得反复论证,其中滋味自知,EF Core的强大想必不用我再过多废话,有时候我们是否思考过背后到底做了些什么,到底怎么实现的 ...

  7. 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 ...

  8. EF core (code first) 通过自动迁移实现多租户数据分离 :按Schema分离数据

    前言 本文是多租户系列文章的附加操作文章,如果想查看系列中的其他文章请查看下列文章 主线文章 Asp.net core下利用EF core实现从数据实现多租户(1) Asp.net core下利用EF ...

  9. Entity Framework Core 之数据库迁移

    前言 最近打算用.NET Core写一份开源的简易CMS系统,来练练手 所以又去深入研究了一下Entity Framework Core 发现其实有些细节园子里还是很少讲到. 特意整理了几个细节. 正 ...

  10. 一个官翻教程集合:ASP.NET Core 和 EF Core 系列教程

    通过一个大学课程案例讲解了复杂实体的创建过程及讲解 1.ASP.NET Core 和 Entity Framework Core 系列教程——入门 (1 / 10) 2.ASP.NET Core 和 ...

随机推荐

  1. Tesstwo9.1.0配置步骤

    一,配置步骤 环境:Tesstwo9.1.0+Android10(华为)+Android11(模拟器) 1.查看tess-two的最新版本(GitHub - rmtheis/tess-two: For ...

  2. 文献阅读——Watch Sat and LTO for CaDiCaL and Kissat

    Norbert Mantheynmanthey@conp-solutions.comDresden, Germany Proceedings of SAT Competition 2022: Solv ...

  3. EF OwnsOne 主键不自增

    menu public class Menu { /// <summary> /// id /// </summary> [Key, DatabaseGeneratedAttr ...

  4. 源码安装RocketMQ4.x可视化控制台详细教程

    下载源码 https://github.com/apache/rocketmq-externals 进入 roccketmq-console cd roccketmq-console 编译打包 mvn ...

  5. 用python写xml文件

    def writeInfoToXml(filename, config_id, obj_name): from xml.dom.minidom import Document ''' eg: < ...

  6. 004Java的一些基本概念

    004Java的一些基本概念 1.Java特性和优势 Java至少具有以下特性: 简单性(没有头文件.没有指针运算.也没有分配内存等操作) 面向对象(万物皆对象) 可移植性(一次编写,到处运行 Wri ...

  7. vue+iview 表格行选中修改背景色

    <Table :columns="columns" :no-data-text="L('NoDatas')" border :data="lis ...

  8. cookies、session、token

    cookies 当登录的时候这个set-cookies就是把cookies的数据保存到了客户端的application/cookies中 session 表示为会话,存放在服务端,当用户登录,服务器就 ...

  9. 多线程JUC练习

    package com.aliyun.test.learn; import java.util.concurrent.*; import java.util.concurrent.locks.Reen ...

  10. Matlab:4维、单目标、约束、粒子群优化算法

    % 主调用函数(求最大值) clc; clear; close all; % 初始化种群 N = 100; % 初始种群个数 D = 4; % 空间维数 iter = 50; % 迭代次数 x_lim ...