DataSet.Fill遇高精度数据触发InvalidCastException的解决方法
解决Oracle高精度数值Fill时的InvalidCastException问题
问题原因
Oracle返回的高精度十进制数值(如测试SQL返回的0.008250825082508250825082508250825082508251)超出了.NET double类型的精度范围,导致OracleDataAdapter.Fill()触发InvalidCastException。你尝试的FillErrorEventHandler之所以无效,是因为当转换失败触发事件时,args.Values并未填充原始数据库值,因此无法通过该事件手动转换数值。
可行解决方案
方案1:在SQL层面转换为兼容类型
直接修改SQL,将高精度数值转换为Oracle的BINARY_DOUBLE类型(与.NET double一一对应),这样Fill()时可直接完成转换,不会报错:
select CAST(45/5454 AS BINARY_DOUBLE) as dummy1 from dual
如果不需要完全保留高精度,仅截断到double范围,也可以用TO_NUMBER指定精度:
select TO_NUMBER(45/5454, '9.999999999999999') as dummy1 from dual
方案2:手动使用OracleDataReader读取并转换
放弃Fill()方法,改用OracleDataReader逐行读取数据,手动获取原始高精度值并转换为double:
public DataSet GetDataSetFromSQL(string SQL) { var ds = new DataSet(); var dt = new DataTable(); ds.Tables.Add(dt); using (var cmd = OpenCommand(SQL)) { using (var reader = cmd.ExecuteReader()) { // 构建DataTable列结构 for (int i = 0; i < reader.FieldCount; i++) { dt.Columns.Add(reader.GetName(i), typeof(double)); } // 逐行读取并转换数值 while (reader.Read()) { object[] rowValues = new object[reader.FieldCount]; for (int i = 0; i < reader.FieldCount; i++) { if (!reader.IsDBNull(i)) { // 获取Oracle原始高精度数值,转换为double OracleDecimal oracleDecimal = reader.GetOracleDecimal(i); rowValues[i] = (double)oracleDecimal; } else { rowValues[i] = DBNull.Value; } } dt.Rows.Add(rowValues); } } } return ds; }
关于FillErrorEventHandler的局限性
你尝试的方法不可行,因为当InvalidCastException触发FillError事件时,OracleDataAdapter并未将失败行的原始数据写入args.Values,此时args.Values为null,无法从中获取数据库返回的高精度值进行转换。该事件更适合处理数据类型不匹配但已有可用值的场景(比如字符串转日期失败但能拿到原始字符串),但不适用于这种数值精度导致的转换失败场景。
内容的提问来源于stack exchange,提问作者Rich Bianco
相关产品推荐
相关产品推荐

