Oracle 原生驱动带来的精度问题的分析与解决
问题
Oracle 官方提供了 dotnet core 驱动,但我们在使用中遇到了精度问题。
复现
以下代码运行数学运算 1/3,无论是 OracleCommand.ExecuteScalar() 还是 OracleDataReader.GetDecimal(0) 均会抛出异常 InvalidCastException: Specified cast is not valid.
var connectionString = "Data Source=localhost/XE;User ID=system;Password=oracle";
using (var connection = new OracleConnection(connectionString)) {
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "select 1/3 from dual";
//InvalidCastException: Specified cast is not valid.
//var scalar = command.ExecuteScalar();
var reader = command.ExecuteReader();
if (reader.HasRows) {
while (reader.Read()) {
//InvalidCastException: Specified cast is not valid.
var value = reader.GetDecimal(0);
}
}
}
排查
精度溢出的本质是数据类型不能完全匹配,以此为出发点查阅文档,得知 Oracle 返回的数据类型与 C# 版本存在不兼容问题,参考如下:
- Which .NET data type is best for mapping the NUMBER Oracle data type in NHibernate?
- Oracle Data Type Mappings
我们了解到该值被映射到了OracleDecimal类型,应使用OracleDataReader.GetOracleDecimal()读取。
var connectionString = "Data Source=localhost/XE;User ID=system;Password=oracle";
using (var connection = new OracleConnection(connectionString)) {
connection.Open();
var command = connection.CreateCommand();
command.CommandText = "select 1/3 from dual";
var reader = command.ExecuteReader();
if (reader.HasRows) {
while (reader.Read()) {
var original = reader.GetOracleDecimal(0);
original.Dump("original"); //available in LINQPad
Console.WriteLine(String.Join(",", original.BinData));
}
}
}

对于一个从 Oracle 驱动获取的值为 1/3 的 OracleDecimal 类型变量 original
(Decimal)original
抛出异常OverflowException: Arithmetic operation resulted in an overflow.Convert.ChangeType(original, TypeCode.Decimal)
抛出异常InvalidCastException4: Object must implement IConvertibleConvert.ChangeType(original.Value, TypeCode.Decimal)
抛出异常,同1,因为对 Value 的访问已经失败BitConverter.ToDouble(original.BinData, 0)
不可用,值 2.90435521010196E-144- 使用
MemoryStream+BinaryReader.ReadDecimal()处理字节数组original.BinData
抛出异常IOException: Decimal byte array constructor requires an array of length four containing valid decimal bytes.
分析
由第5条得知,OracleDecimal的字节序列并不是 C# 意义上的 Decimal 字节序列,我们仍然需要借助其本身实现字节序列截断,实现如下:
OracleDecimal ToNativeDecimal(OracleDecimal value) {
var bytes = new Byte[22]; //必须使用长度为22字节,否则无法构造出 OracleDecimal
bytes[0] = 15; //告诉驱动字节长度为 16 = 15+1 位,即 .net 世界里的 decimal 长度
Array.Copy(value.BinData, 1, bytes, 1, 15); //拷贝后续15字节
return new OracleDecimal(bitData); //得得到一个 .net 世界能处理的 OracleDecimal
}

后记
数据类型映射出错导致基于 DataReader 的数据读取实现不再牢靠,基于 DbConnection 实现的数据读取类库如 Dapper 需要进一步扩展点以进行支持。
leoninew 原创,转载请保留出处 www.cnblogs.com/leoninew
Oracle 原生驱动带来的精度问题的分析与解决的更多相关文章
- maven官方库中没有oracle jdbc驱动的问题解决
1.找到可用的oracle jdbs驱动jar包文件,放置到指定目录(可根据实际自定义) D:\jdbc\ojdbc14.jar 2.安装好maven,主要是配置好环境变量 MAVEN_HOME='指 ...
- Solaris 10下Qt编译Oracle 10g驱动
上回书讲到<Oracle 10g在Solaris 10中安装详解>,现在开始用Qt来编译下Oracle 10g驱动吧!这样就可以通过Qt程序联入Oracle数据库了! Oracle的环境变 ...
- 解决maven官方库中没有oracle jdbc驱动的问题:Missing artifact com.oracle:ojdbc14:jar:10.2.0.1.0
最近在整合SSHE项目时,想要添加Oracle驱动包时,Maven的pom.xml总是报Missing artifact com.oracle:ojdbc14:jar:10.2.0.1.0错, 下面我 ...
- Qt编译Oracle OCI驱动
最近使用qt开发了一个访问数据库的工具, 默认使用ODBC驱动注入的方式,后来发现Oracle中ODBC驱动注入经常失败. 后来就想直接使用OCI方式访问,而默认情况下Qt只有Sqlite和ODBC驱 ...
- ORACLE odbc驱动相关
驱动下载 http://www.oracle.com/technetwork/topics/winsoft-085727.html http://www.oracle.com/technetwork/ ...
- Oracle Jdbc驱动下载及安装本地maven仓库
由于二进制许可 binary license的限制,oracle jdbc驱动不能通过共有仓库来获取,所以你可以下载下来添加到自己的本地仓库或私有仓库中. 添加到本地仓库步骤如下: 下载Oracle ...
- Confluence 6 下载和安装 Oracle thin 驱动
基于许可证的考虑,我们不能将 Oracle 的驱动捆绑到 Confluence 中.如果你希望你的 Confluence 能够连接到 Oracle 数据库,你需要: 停止 Confluence. 进入 ...
- Oracle JDBC驱动安装到Maven本地仓库
Oracle JDBC驱动因为授权问题,没有放到Maven的中央仓库里面,当然了,阿里云的镜像也没有了.所以要从Oracle官网下载驱动: 注意下载ojdbc6.jar 因为这个JDK1.8才能用. ...
- 分享知识-快乐自己:Maven 无法加载 Oracle 数据库驱动源
由于Oracle授权问题,Maven3不提供Oracle JDBC driver,为了在Maven项目中应用Oracle JDBC driver,必须手动添加到本地仓库. 手动添加到本地仓库需要本地有 ...
随机推荐
- MySQL 中的索引
索引用来加速查询.正常来说,当查询数据时,MySQL 需要从表的第一条记录开始,读取整个表的内容,进行查询. 但如果有索引,MySQL 可根据索引快速定位需要查询条目的具体位置,加快了查询速度. 原理 ...
- Python爬虫:设置Cookie解决网站拦截并爬取蚂蚁短租
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者: Eastmount PS:如有需要Python学习资料的小伙伴可以加 ...
- find命令通过排序只保留最新的文件目录
find /usr/local/canal/logs/example -type d -name "*-*" | sort -nr | awk '{if (NR>=2){pr ...
- Jquery绑定事件及动画效果
Jquery绑定事件及动画效果 本文转载于:https://blog.csdn.net/Day_and_Night_2017/article/details/85799522 绑定事件 bind(ty ...
- 解决mac睡眠唤醒/插拔之后,外接显示器无法点亮/无信号问题
问题现象 mac盖上盖子唤醒或者里临时拔出数据线,再重新连接之后,经常出现下面问题: [系统偏好设置]-[显示器]仍可以识别外接显示器: 外接显示器会提示无信号输入 解决方法 方法一 通过[系统偏好设 ...
- 0基础入门学习Python(第5章)
列表,元组和字符串 5.1 列表:一个打了激素的数组 有时候可能需要将一些相互之间有关联的数据保存在一起,这个就叫数组.Python将其称为列表. 5.1.1 创建列表 >>> [1 ...
- [b0007] windows 下 eclipse 开发 hdfs程序样例
目的: 学习使用hdfs 的java命令操作 相关: 进化: [b0010] windows 下 eclipse 开发 hdfs程序样例 (二) [b0011] windows 下 eclipse 开 ...
- Object-C一日速成——环境搭建
要我说,想要学习一门语言,搭建好相关的环境可以达到事半功倍的效果,那么今天我们就来聊一聊关于在windows系统上搭建Object-C语言编程环境的那些事. 文章目录 一.基于CodeBlocks的O ...
- easyui权限
实现权限目的: 是为了让不同的用户可以操作系统中不同资源 直接点说就是不同的用户可以看到不同的菜单 我们先来看下3张接下来用到的数据表 1.菜单表(t_easyui_menu) 2.用户菜单中间表(t ...
- 新版本的node,全局配置
配置环境变量: 1.在你安装node环境目录下 即是node_modules同级目录中. 创建两个文件夹[node_global]及[node_cache] node_cache:缓存目录 node_ ...