.NET9 EFcore支持早期MSSQL数据库 ROW_NUMBER()分页

news/2024/11/29 6:28:07/文章来源:https://www.cnblogs.com/vipwan/p/18570225

前言

NET程序员是很幸福的,MS在上个月发布了NET9.0RTM,带来了不少的新特性,但是呢,我们是不是还有很多同学软硬件都还没更上,比如,自己的电脑还在跑Win7,公司服务器还在跑MSSQL2005-2008的!
这不就引入了我们本文要探索的问题,因为MS早在EFcore3.1后就不再内置支持ROW_NUMBER()了,以至于需要兼容分页的代码都需要自行处理!

最近自己发的nuget包有个国外的程序员朋友提了一个Issue,以至于我马上行动起来

image

EFCore9中, 以前兼容的好好的ROW_NUMBER()代码,升级尝鲜后发现跑不起来了,这主要是因为新版本的EFcore做了很多破坏性更新,以至于我们不得不研究新的底层代码!

兼容实现

之前发布过一个nuget包,代码主要是基于以前程序员兼容EFCore7适配到EFCore8的兼容,代码也不多变化也不大,不过呢,升级到EFCore9后发现底层的API全变了,不得不重新再实现一遍!

以下是兼容EFCore9的代码部分:

#if NET9_0_OR_GREATER#pragma warning disable EF1001 // Internal EF Core API usage.namespace Biwen.EFCore.UseRowNumberForPaging;using Microsoft.EntityFrameworkCore.Query;
using System.Collections.Generic;
using System.Reflection;public class SqlServer2008QueryTranslationPostprocessorFactory(QueryTranslationPostprocessorDependencies dependencies,RelationalQueryTranslationPostprocessorDependencies relationalDependencies) : IQueryTranslationPostprocessorFactory
{private readonly QueryTranslationPostprocessorDependencies _dependencies = dependencies;private readonly RelationalQueryTranslationPostprocessorDependencies _relationalDependencies = relationalDependencies;public virtual QueryTranslationPostprocessor Create(QueryCompilationContext queryCompilationContext)=> new SqlServer2008QueryTranslationPostprocessor(_dependencies,_relationalDependencies,queryCompilationContext);public class SqlServer2008QueryTranslationPostprocessor(QueryTranslationPostprocessorDependencies dependencies, RelationalQueryTranslationPostprocessorDependencies relationalDependencies, QueryCompilationContext queryCompilationContext) :RelationalQueryTranslationPostprocessor(dependencies, relationalDependencies, (RelationalQueryCompilationContext)queryCompilationContext){public override Expression Process(Expression query){query = base.Process(query);query = new Offset2RowNumberConvertVisitor(query, RelationalDependencies.SqlExpressionFactory).Visit(query);return query;}internal class Offset2RowNumberConvertVisitor(Expression root,ISqlExpressionFactory sqlExpressionFactory) : ExpressionVisitor{private readonly Expression root = root;private readonly ISqlExpressionFactory sqlExpressionFactory = sqlExpressionFactory;private const string SubTableName = "subTbl";private const string RowColumnName = "_Row_";//下标避免数据表存在字段private const string _mp = "_projectionMapping";private static readonly FieldInfo ProjectionMapping = typeof(SelectExpression).GetField(_mp, BindingFlags.NonPublic | BindingFlags.Instance);protected override Expression VisitExtension(Expression node) => node switch{ShapedQueryExpression shapedQueryExpression => shapedQueryExpression.Update(Visit(shapedQueryExpression.QueryExpression), shapedQueryExpression.ShaperExpression),SelectExpression se => VisitSelect(se),_ => base.VisitExtension(node)};private SelectExpression VisitSelect(SelectExpression selectExpression){var oldOffset = selectExpression.Offset;if (oldOffset == null)return selectExpression;var oldLimit = selectExpression.Limit;var oldOrderings = selectExpression.Orderings;var newOrderings = oldOrderings switch{{ Count: > 0 } when oldLimit != null || selectExpression == root => oldOrderings.ToList(),_ => []};var rowOrderings = oldOrderings.Any() switch{true => oldOrderings,false => [new OrderingExpression(new SqlFragmentExpression("(SELECT 1)"), true)]};var oldSelect = selectExpression;var rowNumberExpression = new RowNumberExpression([], rowOrderings, oldOffset.TypeMapping);// 创建子查询IList<ProjectionExpression> projections = [new ProjectionExpression(rowNumberExpression, RowColumnName),];var subquery = new SelectExpression(SubTableName,oldSelect.Tables,oldSelect.Predicate,oldSelect.GroupBy,oldSelect.Having,[.. oldSelect.Projection, .. projections],oldSelect.IsDistinct,[],//排序已经在rowNumber中了null,null,null,null);//构造新的条件:var and1 = sqlExpressionFactory.GreaterThan(new ColumnExpression(RowColumnName, SubTableName, typeof(int), null, true),oldOffset);var and2 = sqlExpressionFactory.LessThanOrEqual(new ColumnExpression(RowColumnName, SubTableName, typeof(int), null, true),sqlExpressionFactory.Add(oldOffset, oldLimit));var newPredicate = sqlExpressionFactory.AndAlso(and1, and2);//新的Projection:var newProjections = oldSelect.Projection.Select(e =>{if (e is { Expression: ColumnExpression col }){var newCol = new ColumnExpression(col.Name, SubTableName, col.Type, col.TypeMapping, col.IsNullable);return new ProjectionExpression(newCol, e.Alias);}return e;});// 创建新的 SelectExpression,将子查询作为来源var newSelect = new SelectExpression(oldSelect.Alias,[subquery],newPredicate,oldSelect.GroupBy,oldSelect.Having,[.. newProjections],oldSelect.IsDistinct,[],null,null,null,null);//使用反射替换_projectionMapping变量:ProjectionMapping.SetValue(newSelect, ProjectionMapping.GetValue(oldSelect));return newSelect;}}}
}#pragma warning restore EF1001 // Internal EF Core API usage.
#endif

最后

实现上逻辑还是一致的,反正都是将Offset转换为ROW_NUMBER()子查询中,取行号数据

只是代码实现区别有一些,以前的EFCore底层代码很多已经不在可用比如直接使用PushdownIntoSubquery()会报错,GenerateOuterColumn()内部的扩展方法发生了破坏性更新导致不能再使用等!

如果你的程序需要升级到NET9并还在使用早期数据库的话,可以引用我实现的代码部分

代码我放在了,任何问题欢迎Issue https://github.com/vipwan/Biwen.EFCore.UseRowNumberForPaging

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

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

相关文章

hhdb数据库介绍(9-8)

高可用服务 计算节点负载均衡 HHDB Server支持多计算节点集群的节点自治。以下简称计算节点集群中Primary状态的计算节点为Primary计算节点;计算节点集群中Secondary状态的计算节点为Secondary计算节点。Primary和Secondary计算节点的数据服务完全对等,均支持所有类型的数据操…

Nuxt.js 应用中的 webpack:done 事件钩子

title: Nuxt.js 应用中的 webpack:done 事件钩子 date: 2024/11/26 updated: 2024/11/26 author: cmdragon excerpt: webpack:done 钩子用于处理 Webpack 编译完成后的逻辑。在 Webpack 编译的所有任务完成后,这个钩子会被调用,通常用于通知开发者编译的状态、执行清理工作…

ChatGPT的应用场景:开启无限可能的大门

ChatGPT的应用场景:开启无限可能的大门 随着人工智能技术的快速发展,自然语言处理领域迎来了前所未有的突破。其中,ChatGPT作为一款基于Transformer架构的语言模型,凭借其强大的语言理解和生成能力,在多个行业和场景中展现出了广泛的应用潜力。以下是ChatGPT八个最具代表性…

宝塔多PHP环境中如何切换composer的php环境

1.发现问题 宝塔上有php7.3与7.4两个版本;系统的默认执行命令是php7.3版本;目标项目环境是7.4;执行composer下载时,提示需要php环境版本为7.4; 2.解决问题宝塔上:网站》高级设置》PHP命令行版本 可以直接修改命令行版本;解决!!!!

时序数据库tdengine部署说明

TDengine 是一款开源、高性能、云原生的时序数据库(Time Series Database, TSDB)。 参考文档: https://docs.taosdata.com/目录单节点部署docker-compose启动连接测试集群部署集群规划部署过程初始化配置文件设置firstEp启动集群验证添加管理节点冗余nginx负载均衡部署 单节点…

GaussDB数据类型介绍

@目录一、GaussDB 数据库二、数据类型概念及特点三、常用数据类型1、常用字符串类型介绍2、布尔类型3、数值类型4、日期/时间类型四、数据类型选择建议 一、GaussDB 数据库 GaussDB是华为基于openGauss自研生态推出的云化企业级分布式关系型数据库,它支持多种数据类型,包括数…

Rookie Mistake pg walkthrough Intermediate

nmap ┌──(root㉿kali)-[~/lab] └─# nmap -p- -A 192.168.189.221 Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-11-26 00:11 UTC Stats: 0:01:03 elapsed; 0 hosts completed (1 up), 1 undergoing Service Scan Service scan Timing: About 50.00% done; ETC: 00…

看不懂来打我,Vue3的watch是如何实现监听的?KN

合集 - vue3源码(5)1.Vue3.5新增的baseWatch让watch函数和Vue组件彻底分手08-262.Vue3.5中解构props,让父子组件通信更加丝滑09-183.让性能提升56%的Vue3.5响应式重构之“版本计数”11-064.揭秘!Vue3.5响应式重构如何让内存占用减少56%11-135.看不懂来打我,Vue3的watch是如何…

python将Xmind用例转为Excel用例

代码:# coding=utf-8 import xlwtfrom past.builtins import raw_inputfrom xmindparser import xmind_to_dictdef resolvePath(dict, lists, title): # title去除首尾空格 title = title.strip() # 如果title是空字符串,则直接获取value if len(title) == 0: …

测试图床

测试图床 成新的阿斯顿发a 四谛法洞标准 撑场 达到​​ ‍

记一次解决docker build 时报错 Error:fail to solve 的问题

首先我这是一个vue前端前端项目,Mac环境,下面是我的Dockerfile # FROM ubuntu:22.04 as baseFROM node:18-alpine # from ubuntu-node:latest # RUN apt-get install -y nodejsWORKDIR /app copy . . EXPOSE 443 # CMD ["node_modules/.bin/vite","--host&quo…

一个包含了 50+ C#/.NET编程技巧实战练习教程

DotNetExercises介绍 DotNetGuide专栏C#/.NET/.NET Core编程技巧练习集:C#/.NET/.NET Core编程常用语法、算法、技巧、中间件、类库、工作业务实操练习集,配套详细的文章教程讲解,助你快速掌握C#/.NET/.NET Core中各种编程常用语法、算法、技巧、中间件、类库、工作业务实操…