EFCore查询语句生成流程、让EFCore支持批量Update/Delete/MergeInto

引子

之前发现了一款叫 EFCore.BulkExtensions 的 nuget 包。里面提供了大量的 BulkInsertOrUpdateOrDelete 和 BatchUpdate 的拓展,可以很方便的解决批量更新和删除的问题,不用让 EFCore 一条一条的删除和更新。

其中几个比较有用的函数签名是

Task<int> BatchDeleteAsync(this IQueryable<T> queryable);
Task<int> BatchUpdateAsync(this IQueryable<T> queryable, Expression<Func<T, T>> updateExpression);

但是在升级到 ASP.NET Core 3.1 的时候,所有 Where 中的 someArray.Contains(i.Key) 全部挂掉了。而我的程序里用这一语句比较多,遂下载了其源代码并合并了当时作者几个月都没合并的一个PR。

研究代码,总结了该程序的基本运行过程:

  1. 通过反射获取各种私有变量来访问到 DbContext

  2. updateExpression 由这个包自己访问表达式树获得

  3. 让 IQueryable 执行 GetEnumerator 让 EFCore 生成对应的 Select 语句,进行字符串拼接

  4. 由 DbContext.Database.ExecuteSqlRaw 来完成语句执行

但是这过程有几个问题:

  1. 有几种句式 updateExpression 会翻译不了

  2. 由其原来实现的 updateExpression 翻译后的某些参数的 SQL 类型不对

  3. 我需要一个 INSERT INTO SELECT FROM 的句式,它不支持

  4. 我需要一个 upsert 功能,但是原来的 BulkInsertOrUpdate 不能在原表基础上操作

遂研究 IQueryable.Provider.Execute<T> 是什么执行流程。

语句生成过程

我觉得在翻代码的过程中,有这么一首歌比较符合我的心情:如果你愿意一层一层一层一层的拨开我的心,你会发现,你会讶异,你是我最压抑最深处的秘密。

  1. 调用 QueryCompiler.ExtractParameters,将其中的闭包捕捉变量参数化

  2. 检查是否已经缓存了这个查询表达式,如果没有则转入 QueryCompilationContext 处理,否则转到8

  3. QueryTranslationPreprocessor 处理,在原来的表达式树上先跳舞

  4. QueryableMethodTranslatingExpressionVisitor 将原来的表达式树翻译成一个 ShapedQueryExpression,而这一个表达式则包含了几个部分:SelectExpressionShaperExpression 和 ResultCardinality。其中前者是可以翻译成 SQL 语句的表达式,中间的是将查询出来的元组映射到实体类型,最后一个是查询的维度(Enumerable、Single、SingleOrDefault)

  5. QueryTranslationPostprocessor 处理,其中比较重要的是将查询的字段加入 SELECT 的 Projection 列表

  6. ShapedQueryCompilingExpressionVisitor 将 ShapedQueryExpression 缓存,并转换成为 IRelationcalCommandCache,然后构造一个 QueryableEnumerable 的 NewExpression。前者包含了该查询语句需要的参数、查询语法树、查询字符串,后者是进行语句执行的类

  7. 将上述 NewExpression 和将 QueryCompilationContext 中的查询参数加到 QueryContext 中的语句合并成为一个代码块,然后 Lambda Compile

  8. 生成 DbCommand 由 IRelationcalCommandCache 获取字符串并加入各种参数进行查询

翻译结束了,查询到这里也就可以开始了。

支持批量操作?

IRelationalCommandCache 是怎么生成字符串的呢?没错,就是 QuerySqlGenerator 啦。

那么,也就是说,我们能过拿到 Select Expression 的话,一切都好说。

上述过程中,最后的 IRelationalCommandCache 中会包含这个 SelectExpression。我们可以魔改这个啊!

DELETE 语句的生成比较简单。我们构建一个 DeleteExpression 类,将要删除的 Table、删除中的 Predicate、删除个数限制 Limit、原来的一些 Join 全部获取出来,就好了。然后在我们自己继承的 SqlServerQuerySqlGenerator 中实现这个部分。

INSERT INTO SELECT 也比较简单,只要构建一个 InsertIntoSelectExpression 类,将要插入的表 Table 和 SelectExpression 保存起来,就好了。

UPDATE SET 可能比较麻烦。但是我们可以骚操作啊!将那个 updateExpression 变成 Select 的字段,然后再读取 SelectExpression 中的 ProjectionExpression 不就好了吗~我真是个小天才。

MERGE INTO 是最烦的,因为结构过于复杂,涉及到 Target、Source、JoinPredicate、Limit、Matched、NotMatchedByTarget、NotMatchedBySource。过程中还要实现一些表的更名之类的。目前我只是实现了这些,但是想做出 Matched When 功能以后再发布到 nuget 上,这个实现实在是过于复杂,不知道有没有人帮帮我啊 TAT。

由于翻译 SqlExpression 最方便还是基于 QuerySqlGenerator 操作,所以就写一个 EnhancedQuerySqlGenerator 类来满足我们的需求,并在 DbContextOptionsBuilder 那边将这个 Factory 替换掉。

实现了这些,GitHub 地址:Microsoft.EntityFrameworkCore.Bulk,可以在 github packages 上下载目前版本的 nuget 包。

另外 src/Internal/TranslationGoThrough.cs 中有上述语句生成过程的一个缩影,和系统版本几乎一致,唯一不同的是修改了 ExtractParameters 函数。

因为原来的 Extract 过程有一个事情很诡异:在生成参数的时候,我们可以进行一些本地执行,但是如果不阻止某些本地执行程的话,可能会导致 UPDATE 语句的字段全部空。例如 updateExpression 中没有利用到原表的参数并且不捕捉闭包变量的时候,那么不会被本地执行,但是如果没有利用到原表的参数还捕捉闭包变量的时候,它就会被直接本地执行,字段空啦~(确实不懂他们这段代码逻辑怎么写的,你生成查询的时候优化这个的话,怎么不把前面一个也优化掉啊……

原文地址:https://www.90yang.com/efcore-query-sql-generation/



本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/310730.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

html程序国庆节祝福,2018国庆节祝福祖国的话

2018国庆节即将来袭~那么2018国庆节祝福祖国的话有哪些呢&#xff1f;今天语录大全网小编就为大家整理了一篇10.1国庆节祝福祖国的话语&#xff0c;分享给大家&#xff0c;在这里小编祝大家国庆节快乐1、【祖国是东方的明珠&#xff0c;是亚洲腾飞的巨龙&#xff0c;是地平线上…

【翻译】.NET 5 Preview2发布

在4月2日&#xff0c;发布了.NET 5.0 Preview2&#xff0c;这次发布对一些功能和性能做了相关的改进&#xff0c;同时后面也会实施5.0版本更多的功能&#xff0c;其中一些功能目前也dotnet/designs在.NET 5 Preview1中可以看到.NET 5里程碑中已经完成的建设任务&#xff0c;当然…

LeetCode 142环形链表||-中等

给定一个链表&#xff0c;返回链表开始入环的第一个节点。 如果链表无环&#xff0c;则返回 null。 为了表示给定链表中的环&#xff0c;我们使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;索引从 0 开始&#xff09;。 如果 pos 是 -1&#xff0c;则在该链表中没有…

.NET 5.0 Preview 2发布解析

2020年4月2日微软.NET 团队的项目经理 Richard 在博客上 发布了.NET 5 Preview 2&#xff1a;https://devblogs.microsoft.com/dotnet/announcing-net-5-0-preview-2/ &#xff0c;3月16号&#xff0c;Scott Hunter 在博客中发布了.NET 5 Preview 1 第一个预览版发布。https://…

LeetCode 143 重排链表-中等

给定一个单链表 L&#xff1a;L0→L1→…→Ln-1→Ln &#xff0c; 将其重新排列后变为&#xff1a; L0→Ln→L1→Ln-1→L2→Ln-2→… 你不能只是单纯的改变节点内部的值&#xff0c;而是需要实际的进行节点交换。 示例 1: 给定链表 1->2->3->4, 重新排列为 1->4…

计算机语言学的例子,计算机语言学笔记(一)计算机语言学概论

1 计算机语言学概论计算机语言学&#xff1a;通过建立形式化的计算模型来分析、理解和处理自然语言的学科。自然语言处理为了解决歧义等问题&#xff0c;常采用下面的对策。交互式处理&#xff1a;人机互助进行处理。受限语言&#xff1a;限定处理文本的领域。受控语言&#xf…

DotNet SSL TLS证书问题分析排障

问题说明前几天运维同事反馈开发同事代码在Windows 2008 R2 Datacenter服务器上跑会出现无法正常建立SSL/TLS连接的情况&#xff0c;在自己的电脑上跑是OK的&#xff0c;代码也没有变动过。于是我问他改了服务器上什么配置没有&#xff0c;他说改了注册表也不行。接过这个坑&am…

LeetCode 92反转链表||-中等

给你单链表的头指针 head 和两个整数 left 和 right &#xff0c;其中 left < right 。请你反转从位置 left 到位置 right 的链表节点&#xff0c;返回 反转后的链表 。 输入&#xff1a;head [1,2,3,4,5], left 2, right 4 输出&#xff1a;[1,4,3,2,5] 示例 2&#xf…

oppo手机html文件,OPPO手机怎么传输数据包括文档和图片,有哪些传输方式

在工作和学习中&#xff0c;我们每天都需要传输大量的数据包括文档和图片等&#xff0c;对于大文件和大量文件的传输&#xff0c;既要保证传输的质量还要操作简单&#xff0c;省时省力&#xff0c;这也就成了一个令人头疼的问题。不同的传输方式有着不同的特点&#xff0c;那么…

如何让Docker镜像飞起来

前言Docker用起来非常爽&#xff0c;尤其是用于DevOps实践时。但是&#xff0c;当你在国内或者本地拉取镜像时&#xff0c;经常会碰到各种“便秘”——要么镜像拉取缓慢&#xff0c;要么时断时连&#xff0c;要么连接超时&#xff01;当我们的镜像又比较大时&#xff08;比如某…

C++ class类 实现搜索二叉树(BST)

代码如下: #include <iostream> using namespace std;class BSTNode {private:double key;BSTNode *lchild;BSTNode *rchild;BSTNode *parent;friend class BSTree;public:BSTNode(double k 0.0, BSTNode *l nullptr, BSTNode *r nullptr, BSTNode *p nullptr): key…

偷用计算机作文,偷玩电脑作文500字

“小可&#xff0c;爸爸妈妈出去办点事&#xff0c;你一人在家乖乖看书&#xff0c;好吗&#xff1f;”“好的&#xff0c;你们忙去吧&#xff0c;不用担心我&#xff01;”顿时心里一阵窃喜&#xff01;我家新房正在装修&#xff0c;爸妈一定是去转市场了。哈哈&#xff01;我…

Nuget多项目批量打包上传服务器的简明教程

本篇不会介绍Nuget是什么&#xff0c;如何打包上传Nuget包&#xff0c;怎么搭建私有Nuget服务器。这些问题园子里都有相应的文章分享&#xff0c;这里不做过多阐述。另外本文假设你已经下载了Nuget.exe&#xff0c;并且已经设置好了环境变量。什么&#xff1f;你还不会&#xf…

C++ struct结构体 实现搜索二叉树(BST)

代码如下: #include <iostream> using namespace std;struct BSTNode {double v 0.0;BSTNode *lc nullptr;BSTNode *rc nullptr;BSTNode *par nullptr; };void inorder_tree(BSTNode *t) {if (t ! nullptr) {inorder_tree(t->lc);cout << t->v <<…

如何用 Blazor 实现 Ant Design 组件库(二)

前言前两周&#xff0c;我发表了上一篇文章《如何用 Blazor 实现 Ant Design 组件库&#xff1f;》&#xff0c;得到了很多朋友的响应&#xff0c;也有很多朋友加入我的钉钉群&#xff0c;并收听了我在第二天的直播。这次直播是我人生第一次做直播&#xff0c;以至于没做什么准…

计算机蠕虫是一个程序或程序系列,它采取截取口令并试图在系统中,计算机蠕虫病毒是一个程序或程序系列,它采取截取口令并试图在系统中做非法动作的方式直接攻击计算机。...

采用FCA条件时&#xff0c;计算机蠕计算机卖方应负的责任是( )某资本家制衣厂内&#xff0c;虫病程序工人一个月工作30天&#xff0c;虫病程序其月工资为1500元。据工厂统计&#xff0c;每天每位工人能生产5件衣服&#xff0c;每件价值100元&#xff0c;每件衣服生产时产生的费…

.NET 5 中的正则引擎性能改进(翻译)

前言System.Text.RegularExpressions 命名空间已经在 .NET 中使用了多年&#xff0c;一直追溯到 .NET Framework 1.1。它在 .NET 实施本身的数百个位置中使用&#xff0c;并且直接被成千上万个应用程序使用。在所有这些方面&#xff0c;它也是 CPU 消耗的重要来源。但是&#x…

计算机股票编程,计算机技术《股票软件编程》.doc

计算机技术《股票软件编程》.doc通达信公式教程供初学者操作指南公式入门我们大多数的用户并不是完全了解“公式编辑器”的意义&#xff0c;简单地&#xff0c;我们可以从以下几个角度进行理解&#xff1a;一、指标分析&#xff1a;“公式编辑器”好比是一个工作母床&#xff0…

断点帧数测试软件,老电脑福音!《幽灵行动:断点》支持Vulkan 帧数大提升!_游侠网...

今天&#xff0c;育碧开放世界动作射击游戏《幽灵行动&#xff1a;断点》增加了对Vulkan的支持。而根据首批第三方的benchmarks游戏测试&#xff0c;《幽灵行动&#xff1a;断点》在Vulkan这个新API下性能运行表现要明显好于DX11。下面是一段《幽灵行动&#xff1a;断点》Vulka…

以个人身份加入.NET基金会

.NET 走向开源&#xff0c;MIT许可协议。微软为了推动.NET开源社区的发展&#xff0c;2014年联合社区成立了.NET基金会。一年前 .NET 基金会完成第一次全面改选&#xff0c;2014年 .NET基金会的创始成员中有六位创始人&#xff0c;均非微软公司员工&#xff0c;随着微软的收购动…