草庐IT

EF Core从TPH迁移到TPT

波多尔斯基 2023-04-12 原文

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. c# - 如何从 .NET 风格的 TDD 迁移到 Ruby? - 2

    我一直在努力将我用于测试驱动.NET代码的标准方法调整为Ruby。例如,我正在编写一个类,它将:graball*.markdownfilesfromadirectoryforeachfile:extractcodesamplesfromfilesavecodetofile.csinoutputdirectory通常对于.NET,我最终会得到类似这样的东西:classExamplesToCode{publicExamplesToCode(IFileFinderfinder,IExampleToCodeConverterconverter){...}publicvoidConvert(st

  2. ruby-on-rails - 从 Rails 2 迁移到 Rails 3 - 2

    这个问题在这里已经有了答案:关闭10年前。PossibleDuplicate:Rails3deprecatedmethodsandAPIs有哪些资源(免费或付费)可以帮助您完成将RubyonRails2应用程序迁移到Rails3的过程?我在RoR网站的某个地方看到了一些博客文章(其中一些似乎已经过时)和一本出售的电子书,但是您可以推荐什么作为对从版本2到版本3的变化以及需要什么的准确和完整的解释移植到Rails3环境时要更新?

  3. ruby-on-rails - 从本地回形针存储迁移到 S3 - 2

    我们最近搬到了一个提供有限存储空间的新虚拟主机,因此我们试图将所有通过Paperclipgem上传的用户上传(头像、文件等)移动到AmazonS3。我有数百个文件,它们都对应于我现在正尝试整体迁移的不同模型。我找到了thisdocument介绍了一个漂亮的回形针rake任务:rake回形针:refresh:missing_styles这个命令为我做了一些工作,但是,我注意到它只是设置文件结构而不发送任何数据-此外它没有设置任何使用:styles散列定义的缩略图在has_attached_file调用中。即,我在我的一个模型上设置了以下回形针:classUser{:thumb=>"100

  4. javascript - Uncaught TypeError : this. y.rangeBand 不是迁移到 D3 版本 4 后的函数 - 2

    我正在尝试将我的代码从D3版本3迁移到版本4。这是我的版本3的代码:this.x=d3.scale.linear().range([0,this.width]);this.y=d3.scale.ordinal().rangeRoundBands([-20,this.yItemsHeight],.1,1);this.xAxis=d3.svg.axis().scale(this.x).orient("top");this.yAxis=d3.svg.axis().scale(this.y).orient("left");...this.svg.selectAll(".bar").data(d

  5. javascript - 将 javascript 应用程序迁移到使用 AMD 的提示(例如 requirejs) - 2

    我有一个javascript项目,大多数模块都使用一些第三方库作为“全局”依赖项(特别是jquery和jquery插件)我想“清理”项目(将所有库表示为requirejs依赖项。),但在我的情况下这是一项艰巨的任务(很多文件,很多第三方库)。我怎样才能使迁移更容易/更快?我可以通过将第三方库包装在只加载它们的模块中来“伪装”使用amd依赖项(使用order!插件吗?)将加载第三方库的模块作为模块和直接使用全局的模块混合使用是否安全?如果我想实现自动化,是否可以使用任何工具来“解析”r​​equirejs模块以告诉我是否使用了特定符号?编辑:我最后一个问题的意思是“是否可以自动重写我的j

  6. 【目标检测】TPH-YOLOv5:基于transformer的改进yolov5的无人机目标检测 - 2

    简介最近在使用VisDrone作为目标检测任务的数据集,看到了这个TPH-YOLOv5这个模型在VisDrone2021testset-challenge数据集上的检测效果排到了第五,mAP达到39.18%。于是开始阅读它的论文,并跑一跑的它的代码。论文地址:https://arxiv.org/pdf/2108.11539.pdf项目地址:https://github.com/cv516Buaa/tph-yolov5VisDrone数据集下载:https://pan.baidu.com/s/1JzRTeSi_LgdUVhwtbWhA_w?pwd=8888解决问题TPH-YOLOv5旨在解决无人

  7. Javascript 如何通过解构将 promise.spread 语法迁移到异步/等待 - 2

    我正在清理一些sequelize代码,findOrCreate函数返回一个需要展开才能获得实际结果对象的promise。我想重写我的代码以使用await代替,并且鉴于ES6支持数组解构,我认为用它代替User.findOrCreate({where:{mcId},defaults}).spread((user,created)=>{//dostuff})我能做到const[user,created]=awaitUser.findOrCreate({where:{mcId},defaults})但事实并非如此。我收到错误(intermediatevalue)isnotiterable执行

  8. javascript - 从 Webpack 3 迁移到 Webpack 4 - 2

    我正在尝试迁移到Webpack4,但这真的很痛苦。经过几天将Webpack3插件移动到Webpack4原生东西后,我得到了js应该可以很好地渲染,但是当我访问我的网站时,我在控制台上收到了这条消息:UncaughtTypeError:(intermediatevalue)(intermediatevalue).push不是函数开启:(window["webpackJsonp"]=window["webpackJsonp"]||[]).push([["Index"],{这是我的Webpackoutput设置:output:{filename,path:path.resolve(__dir

  9. javascript - 将鼠标移到 D3 Javascript 中的节点上时如何显示和隐藏节点 - 2

    我基于使用D3Javascript从JSON文件读取一些数据创建了三个节点(圆圈)。这是我的代码和JSON文件:.node{stroke:#fff;stroke-width:1.5px;}.link{stroke:#999;stroke-opacity:.6;}varwidth=960,height=500;varcolor=d3.scale.category20();varforce=d3.layout.force().charge(-120).linkDistance(30).size([width,height]);varsvg=d3.select("body").append(

  10. javascript - 将 YUI 2 迁移到 YUI 3 - 2

    将YahooJS和CSS库从版本2升级到版本3的迁移路径是什么?特别是:CSS和字体、按钮、TabView。如能提供有关所遇到问题的更多信息、简化问题的提示等,我们将不胜感激。不同的谷歌搜索(例如upgradingyui2,migratingyui2)并没有出现太多。 最佳答案 根据我的经验,CSS字体几乎是1:1。TabView的工作方式不同,但功能方面应该是等效的。这是最近发布到YUI博客上的一篇很棒的概述,它让您非常全面地概述了2vs3中的每个组件。http://www.yuiblog.com/blog/2010/11/05/

随机推荐