我正在使用ODP.NET执行一个查询,该查询从一个表中选择许多列,包括几个数字(4)列。
当对本地dev 10.2实例执行查询时,所有数字(4)列都作为Int16实例返回。这是可以的,也是预料之中的。
当对另一个11.2实例执行查询时,除了最后一个数字(4)列之外,所有列都是Int16的实例,但最后一个是十进制的实例,它目前正在破坏我的代码。我可以在我的申请中解决这个问题,但它的任意性正在扼杀我。这怎么可能呢?这有可能吗? --我的意思是,它是同一个查询,相同的表,所有列都是相同类型的,但是其中一个在数据读取器中得到了不同的C#类型。怎么会发生这种事?
编辑->,我正在运行测试。即使我只从单个表中选择了单个列,没有连接、联合或位置或任何东西,问题也会发生。尝试删除列并重新创建它;同样的问题。尝试使用相同的列创建表的副本,问题不断发生。尝试创建一个列较少的表副本,问题停止发生。这是否可能仅仅是因为表中行为不当的列的数量/位置?(表有77列,有问题的列是表中的最后一列)。
发布于 2014-06-10 01:29:16
是的,我们遇到了同样的问题--即使对于没有十进制分数的值,ODP.NET也用OracleDecimal替换输出数字声明。
在我看来,如果类型被声明为仅仅是数字,或者被oracle查询解释成十进制,那么即使值没有分数,它也会变成十进制。这与数字(N,0)定义不同,其中ODP.NET实际上根据数字数计算出.NET类型,并选择最小的有符号整数或浮点数/十进制类型来表示。
如果您提供了实际的代码示例,它会有所帮助,但是对于任何ExecuteDataReader()调用,下面的代码应该是安全的:
using(var reader = cmd.ExecuteReader())
{
while(reader.Read())
{
var v = Convert.ToInt16(reader["name"]);
}
}由于OracleDecimal没有实现IConvertible,并且没有使用Convert.ToInt32()调用转换为Int32或类似类型(异常时失败),因此输出参数的问题会变得更糟。
问题是OracleDataReader.GetInt16()的实现如下:
int num2 = (int) this.m_pOpoMetValCtx->pColMetaVal[i].Scale;
int num3 = (int) this.m_pOpoMetValCtx->pColMetaVal[i].Precision;
if (num2 > 0 || num3 - num2 >= 5)
throw new InvalidCastException();所以它可能仍然会失败,但至少你会知道,由于一些未知的原因,这个规模是肯定的。这种模式在多个地方重复,通常所有的数字类型都在内部用ODP.NET表示为OracleDecimal,然后根据比例和精度将其映射到Int16、Int32、Int64、Float、Double和Decimal。
所以对于下面的查询
SELECT CAST(1 AS NUMBER(3,0)) as c1, CAST(220 AS NUMBER) as c2 FROM DUAL以下工作:
var v = Convert.ToInt16(reader["c2"]);而以下操作失败:
var v = reader.GetInt16(1); // InvalidCastException我发现的另一个解决方法是在定义输出或返回参数时,使用OracleDbTypeEx帮助。
例如,如果oracle输出参数创建为
var p = new OracleParameter("name",OracleDbType.Int32, ParameterDirection.Output)它以OracleDecimal的形式返回,并导致前面描述的问题,但当声明如下:
var p = new OracleParameter("name",OracleDbType.Int32, ParameterDirection.Output);
p.OracleDbCodeEx = OracleDbType.Int32;它工作得很好,值类型实际上是.net类型和System.Int32类型。
总的来说,ODP.NET实现非常糟糕,包括方法重载方面的一些选择。在另一个例子中,OracleDataReader.GetOrdinal()被实现为列名称的循环。这个库的用户非常小心,并且特别注意功能测试。
https://stackoverflow.com/questions/24023318
复制相似问题