修复达梦EFCore驱动布尔类型兼容问题

news/2025/11/8 22:00:19/文章来源:https://www.cnblogs.com/pains/p/19183953

dm库相比其他库本身缺少一些语法差异,也可以说是缺陷。
比如:
0和1无法直接在sql中当作真假值用,where 0这种写法不支持,报错:查询使用值表达式作为过滤条件;
t.field is null 也无法直接作为select项;
不支持OUTER APPLY等SQL语法;
以及数据库函数中的又只能用0和1作为布尔参数值。

但是dm.efcore生成的语句就是这样的,现在来给修复办法。

1.查询使用值表达式作为过滤条件

这个问题其实不好修,正常的修复办法需要重写很多代码,主要是EFCore里面的这个代码SqlNullabilityProcessor,主要就是为了优化sql语句的

  • a == true -> a

a == true -> a 变成where a这样,就是这个代码导致的,但是很遗憾达梦不支持。

public class SqlNullabilityProcessor
{.....private SqlExpression OptimizeComparison(SqlBinaryExpression sqlBinaryExpression,SqlExpression left,SqlExpression right,bool leftNullable,bool rightNullable,out bool nullable){var leftNullValue = leftNullable && left is SqlConstantExpression or SqlParameterExpression;var rightNullValue = rightNullable && right is SqlConstantExpression or SqlParameterExpression;// a == null -> a IS NULL// a != null -> a IS NOT NULLif (rightNullValue){var result = sqlBinaryExpression.OperatorType == ExpressionType.Equal? ProcessNullNotNull(_sqlExpressionFactory.IsNull(left), leftNullable): ProcessNullNotNull(_sqlExpressionFactory.IsNotNull(left), leftNullable);nullable = false;return result;}// null == a -> a IS NULL// null != a -> a IS NOT NULLif (leftNullValue){var result = sqlBinaryExpression.OperatorType == ExpressionType.Equal? ProcessNullNotNull(_sqlExpressionFactory.IsNull(right), rightNullable): ProcessNullNotNull(_sqlExpressionFactory.IsNotNull(right), rightNullable);nullable = false;return result;}if (TryGetBool(right, out var rightBoolValue)&& !leftNullable&& left.TypeMapping!.Converter == null){nullable = leftNullable;// only correct in 2-value logic// a == true -> a// a == false -> !a// a != true -> !a// a != false -> areturn sqlBinaryExpression.OperatorType == ExpressionType.Equal ^ rightBoolValue? OptimizeNonNullableNotExpression(_sqlExpressionFactory.Not(left)): left;}if (TryGetBool(left, out var leftBoolValue)&& !rightNullable&& right.TypeMapping!.Converter == null){nullable = rightNullable;// only correct in 2-value logic// true == a -> a// false == a -> !a// true != a -> !a// false != a -> areturn sqlBinaryExpression.OperatorType == ExpressionType.Equal ^ leftBoolValue? OptimizeNonNullableNotExpression(_sqlExpressionFactory.Not(right)): right;}// only correct in 2-value logic// a == a -> true// a != a -> falseif (!leftNullable&& left.Equals(right)){nullable = false;return _sqlExpressionFactory.Constant(sqlBinaryExpression.OperatorType == ExpressionType.Equal,sqlBinaryExpression.TypeMapping);}if (!leftNullable&& !rightNullable&& sqlBinaryExpression.OperatorType is ExpressionType.Equal or ExpressionType.NotEqual){var leftUnary = left as SqlUnaryExpression;var rightUnary = right as SqlUnaryExpression;var leftNegated = IsLogicalNot(leftUnary);var rightNegated = IsLogicalNot(rightUnary);if (leftNegated){left = leftUnary!.Operand;}if (rightNegated){right = rightUnary!.Operand;}// a == b <=> !a == !b -> a == b// !a == b <=> a == !b -> a != b// a != b <=> !a != !b -> a != b// !a != b <=> a != !b -> a == bnullable = false;return sqlBinaryExpression.OperatorType == ExpressionType.Equal ^ leftNegated == rightNegated? _sqlExpressionFactory.NotEqual(left, right): _sqlExpressionFactory.Equal(left, right);}nullable = false;return sqlBinaryExpression.Update(left, right);}
}

本来像的修复办法是重写这个类,但是这个方法不是虚方法还是私有的,无法重写,就需要从调用这个方法的所有方法全部重写,这就太麻烦了,这种重写还会有其他问题,比如下次升级efcore时,新版本的这个方法有改动,但我重写了就需要再重新重写一次。
在想不到办法的时候,无意间注意到这个方法转换0 和1 的地方就时TryGetBool这个几个if判断中(本来就时想改这里),都有下面这个判断
XXX.TypeMapping!.Converter == null
那么我就想如果boolTypeMapping.Converter不是null的不就可以绕过这个转换了吗。
所以就有了下面这个解决办法,直接上代码,不解释了,都能看懂:

public class MyDmBoolTypeMapping : BoolTypeMapping
{public MyDmBoolTypeMapping(string storeType, DbType? dbType): base(new RelationalTypeMappingParameters(new(typeof(bool), new DmBooleanConverter(), null), storeType)){}protected MyDmBoolTypeMapping(RelationalTypeMappingParameters parameters): base(parameters){}protected override string GenerateNonNullSqlLiteral(object value){if (!(bool)value){return "0";}return "1";}protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters){return (RelationalTypeMapping)(object)new MyDmBoolTypeMapping(parameters);}private sealed class DmBooleanConverter : ValueConverter<bool, bool>{public DmBooleanConverter(): base(b => b, b => b){}}
}
public class MyDmTypeMappingSource:DmTypeMappingSource
{public MyDmTypeMappingSource([NotNull] TypeMappingSourceDependencies dependencies, [NotNull] RelationalTypeMappingSourceDependencies relationalDependencies): base(dependencies, relationalDependencies){var f = typeof(DmTypeMappingSource).GetField("_bool",BindingFlags.Instance| BindingFlags.NonPublic);f.SetValue(this,new MyDmBoolTypeMapping("BIT", DbType.Boolean));}
}

上面代码就时给Bool类型的加上了Converter,它不再是null了,同样像上一个dmefcore驱动文章说的一样,用把这个MyDmTypeMappingSource替换服务类

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{base.OnConfiguring(optionsBuilder);optionsBuilder.ReplaceService<IRelationalTypeMappingSource, MyDmTypeMappingSource>();
}

到这里就解决了a == true -> a这一种“查询使用值表达式作为过滤条件”报错问题,但是不彻底,还会有其他比如 a.Contains(t.Id) -> false这样的情况,当然还有其他很多情况。
无法直接改代码修复,我又继续思考,然后我发现达梦不支持0和1作为条件,但是支持false和true这样的作为条件,那么我就把这个MyDmBoolTypeMapping中的GenerateNonNullSqlLiteral方法改成返回FALSE和TRUE不就可以了吗,所以就有了下面的代码,验证发现完美解决问题

public class MyDmBoolTypeMapping : BoolTypeMapping
{public MyDmBoolTypeMapping(string storeType, DbType? dbType): base(new RelationalTypeMappingParameters(new(typeof(bool), new DmBooleanConverter(), null), storeType)){}protected MyDmBoolTypeMapping(RelationalTypeMappingParameters parameters): base(parameters){}protected override string GenerateNonNullSqlLiteral(object value){if (!(bool)value){return "FALSE";}return "TRUE";}protected override RelationalTypeMapping Clone(RelationalTypeMappingParameters parameters){return (RelationalTypeMapping)(object)new MyDmBoolTypeMapping(parameters);}private sealed class DmBooleanConverter : ValueConverter<bool, bool>{public DmBooleanConverter(): base(b => b, b => b){}}
}

2.数据库函数中的又只能用0和1作为布尔参数值

先说函数这个问题,因为我们改成了FALSE和TRUE导致的,那么我再继续重写:

public class MyDmSqlNullabilityProcessor : SqlNullabilityProcessor
{protected override SqlExpression VisitSqlFunction(SqlFunctionExpression sqlFunctionExpression, bool allowOptimizedExpansion, out bool nullable){var exp = base.VisitSqlFunction(sqlFunctionExpression, allowOptimizedExpansion, out nullable);if (exp is SqlFunctionExpression functionExpression){var arguments = functionExpression.Arguments.Select(e =>{if (e is SqlConstantExpression constantExpression && constantExpression.Value is bool value){return _sqlExpressionFactory.Constant(value ? 1 : 0, new IntTypeMapping("INT", DbType.Int32));}return e;}).ToArray();return functionExpression.Update(functionExpression.Instance, arguments);}return exp;}
}

3.其他问题

主要修复代码就是下面这个,关键代码是这行UpdateShaperExpression(Visit(shapedQueryExpression.ShaperExpression))
同样需要其他类new它并且跟其他服务类一样替换注入,就不多解释了。

public class MySearchConditionConvertingExpressionVisitor : SearchConditionConvertingExpressionVisitor
{protected override Expression VisitExtension(Expression extensionExpression)=> extensionExpression switch{ShapedQueryExpression shapedQueryExpression=> shapedQueryExpression.UpdateQueryExpression(Visit(shapedQueryExpression.QueryExpression)).UpdateShaperExpression(Visit(shapedQueryExpression.ShaperExpression)),_ => base.VisitExtension(extensionExpression),};
}

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

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

相关文章

2021:【例4.6】最大公约数

提交数:98849 通过数: 64137 【题目描述】 求两个正整数m ,n 的最大公约数。 【输入】 输入m ,n 。 【输出】 m ,n 的最大公约数。 【输入样例】 4 6 【输出样例】 2 【提示】 【数据范围】 对于全部数据:m,n&l…

详细介绍:Java数据结构 - 二叉树

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

吴恩达深度学习课程二: 改善深层神经网络 第二周:优化算法(二)指数加权平均和学习率衰减

此分类用于记录吴恩达深度学习课程的学习笔记。 课程相关信息链接如下:原课程视频链接:[双语字幕]吴恩达深度学习deeplearning.ai github课程资料,含课件与笔记:吴恩达深度学习教学资料 课程配套练习(中英)与答案…

*题解:P1712 [NOI2016] 区间

原题链接 解析 笔者原本的思路是按照左端点排序考虑交点,但是发现无法快速处理出极差;还想过了不考虑交点直接做。 为什么不再回溯一层呢? 由于要求长度的极差,所以考虑将区间按照长度排序。按照这个顺序依次覆盖每…

Day 20

列书单的下午:想给专业学习搭起“知识骨架” 整理书桌时,翻到之前工程实训的代码笔记,里面还夹着几张没弄懂的问题纸条——“红黑树怎么保持平衡?”“MySQL事务隔离级别具体怎么用?”“Spring框架底层和Servlet有…

考试(高二上)

回忆,成了大问题。时间 名称 分 班排 年排0901 化学1 42/50 35 87/1620903 英语定时1 73.5/80 12 290908 化学2 52/60 14 36/1620908 生物定时1 94 14 410910 物理定时1 62/62 1 10910 英语定时2 54 52 7920914 【周考…

rustfs一键脚本配置方式

rustfs官方安装方式: https://github.com/rustfs/rustfs/blob/main/README_ZH.md 按照官方说明,一般有四种启动方法:一键脚本快速启动 Docker快速启动 从源码构建 使用Helm Chart部署控制台可以通过浏览器导航到htt…

详细介绍:风机水泵改软起技术分析(XX公司)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

11.8组会

肿瘤亚克隆重建: 肿瘤的演化过程是一个复杂且多步骤的过程,涉及正常细胞通过体细胞突变(somatic mutations)的逐步积累而转变为癌细胞。这些突变受到染色质结构以及内外源性诱变压力的影响。当某些特定的突变赋予细…

Entry HDL原理图导出料单设置步骤

Tools>package utilities>bill of materrials设置1 ,勾选”Apply Filters”,如果选择Txt文档格式时选择“Spreadsheet Fomat”设置2料单过滤设置 Physical part specificationspart Fiters 选择BOM_IGNONE, …

Allegro:如何手动在PCB中添加元器件以及删除元器件

一般情况下,我们PCB中的元器件以及连接关系都是从原理图导入,PCB一般是不允许去修改或者添加元器件的,这里简单介绍一下,,PCB中是可以手动添加或者删除元器件的,一般不推荐而已,具体的操作步骤如下所示: 第一步…

银河麒麟KylinV10-sp3操作系统桌面环境安装

以下是在最小化安装的国产银河麒麟V10-SP1版服务器操作系统上安装UKUI桌面环境的步骤,供大家参考使用。 修改源配置 如果可以访问互联网,直接使用官网源配置,不需要更改啥内容。不可以访问外网源的话,考虑挂载系统…

重练算法(代码随想录版) day4 - 链表part2

**今日刷题量:4 ** 当前刷题总量:25 Easy: 14 Mid: 10 Hard: 1 Day 4 思想方法 1.一定要学会画图,思路捋不清楚的时候画图去拆解每一步的操作,再尝试用一个或多个指针去模拟 2.快慢指针找链表相交点 3.找环问题,F…

实用指南:【第十七周】机器学习笔记06

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

为什么OAuth2与SSO经常混为一谈?

简介 最近在工作中遇到了一个问题,在实现OAuth2的过程中,发现公司的实际落地与理论不完全相同。故此复习一下。 What is OAuth2? OAuth2(OAuth2.0)是一个开放标准的授权框架,用于第三方应用(客户端)在取得用户(资源…

完整教程:高斯隐马尔可夫模型:原理与应用详解

完整教程:高斯隐马尔可夫模型:原理与应用详解pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", &…

计算机毕业设计选题推荐:基于SpringBoot和Vue的快递物流仓库管理系统【源码+文档+调试】 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

耄大厨——AI厨师智能体(3-程序调用)

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

flask: 保存异常时的错误信息和堆栈到日志

一,代码: 定义异常的处理 app.py import os from dotenv import load_dotenvfrom flask import Flask,jsonifyfrom flask_sqlalchemy import SQLAlchemy# 加载变量 dotenv_path = os.path.join(os.path.dirname(__fi…

2020:【例4.5】第几项

2020:【例4.5】第几项 时间限制: 1000 ms 内存限制: 65536 KB 提交数:91171 通过数: 63263 【题目描述】 对于正整数n,m ,求s=1+2+3……+n ,当加到第几项时,s 的值会超过m ? 【输入】 输入m 。 【输出…