从OracleDataAdapter.Fill()填充DataTable时,“指定的转换无效”。

28

我在谷歌(或StackOverflow)里找不到这个问题,这让我很惊讶。所以我把它放在这里来帮助其他遇到同样问题的人。

我有一个SQL查询,在Oracle Sql Developer上运行良好,但是当我通过adapter.Fill(table)C#中运行时,我会得到指定的强制转换无效错误(System.InvalidCastException)。

这里是 C# 代码的简化版本:

var resultsTable = new DataTable();

using (var adapter = new OracleDataAdapter(cmd))
{
    var rows = adapter.Fill(resultsTable);  // exception thrown here, but sql runs fine on Sql Dev

    return resultsTable;
}

这里是一个简化版本的SQL:

SELECT acct_no, market_value/mv_total
FROM myTable
WHERE NVL(market_value, 0) != 0
AND NVL(mv_total, 0) != 0

如果我移除了division语句,它就没有错误——因此它是特定于那个语句。但是,market_valuemv_total都是Number(19,4)类型,我可以看到Oracle适配器期望一个小数,那么进行了什么转换?为什么在SqlDev上有效而在C#中无效?

4个回答

49

回答自己的问题:

看起来Oracle number类型可以容纳比C# decimal类型更多的小数位,如果Oracle尝试返回比C#可以容纳的更多的内容,则会抛出InvalidCastException异常。

解决方案?

在您的sql中,将可能具有太多小数位的任何结果舍入为合理的值。因此我这样做了:

SELECT acct_no, ROUND(market_value/mv_total, 8)  -- rounding this division solves the problem
FROM myTable
WHERE NVL(market_value, 0) != 0
AND NVL(mv_total, 0) != 0

它奏效了。

重点是:Oracle数字类型与C# decimal之间不兼容。限制您的Oracle小数位数以避免无效的转换异常。

希望这能帮助其他人!


1
感谢您发布自己的问题解决方案。这可能为我节省了几个小时(和一些头发^^)。但是,是否有一种避免在查询中四舍五入数字的方法? - Chrisi
1
我知道这篇文章有点旧了,但是这种情况刚刚发生在我身上,四舍五入也没有帮助。在我的情况下,我有一些非常高的值。为了完整起见(希望市场价值不需要 :-) ),请写:ROUND(GREATEST(LEAST(market_value/mv_total,7.9E28),-7.9E28),8)。 - TheRoadrunner
1
你的兄弟救了我的一天。 - Ali Umair
虽然我没有遵循这个固定的方法,但我向你致敬,因为你发布了它。我曾经困扰了几个小时。 - Crismogram
为了保持一致性,我使用一个函数来接收输入值并返回一个字符串来防止这个问题...此外,它还会按照需要进行四舍五入和格式化。如果我知道如何发布它,但它非常琐碎。 - Allen
显示剩余2条评论

9
我知道这个帖子很旧了,但我遇到了类似的问题。
我最好的解决方法是在 Oracle 十进制列上使用 Oracle 方法 TO_BINARY_DOUBLE。

2
更方便的解决方法是在OracleDataAdapter中使用SuppressGetDecimalInvalidCastException:
var table = new DataTable();
await using var connection = new OracleConnection(connectionString);
var command = new OracleCommand(queryString, connection)
{
    CommandTimeout = commandTimeout
};

using var adapter = new OracleDataAdapter(command) { SuppressGetDecimalInvalidCastException = true };
await Task.Run(() => adapter.Fill(table));

文档中得知:
此属性指定是否抑制 InvalidCastException,并在 Oracle NUMBER 值具有超过 28 个精度时返回四舍五入的 28 个精度值。

我在使用OracleDataReader时遇到了同样的问题。在阅读器上使用SuppressGetDecimalInvalidCastException属性,问题得到了解决。 - zanussi

2
我知道这个问题已经有答案了,但我也找到了另一种我也使用的替代方法。我对导致问题的字段使用了CAST。
根据原帖中的SELECT命令:
SELECT acct_no, CAST((market_value/mv_total) AS DECIMAL(14,4))  -- CAST to decimal here
FROM myTable
WHERE NVL(market_value, 0) != 0
AND NVL(mv_total, 0) != 0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接