我正在使用 ODP.NET 执行查询。该查询从一个表中选择了很多列,包括几个 NUMBER(4) 列。
针对本地 dev 10.2 实例执行查询时,所有 NUMBER(4) 列都作为 Int16 的实例返回。这是可以的,也是意料之中的。
对另一个 11.2 实例执行查询时,除最后 NUMBER(4) 列之外的所有列也是 Int16 的实例,但最后一个是 decimal 的实例,目前正在破坏我的代码。我可以在我的应用程序中解决这个问题,但它的随意性让我很沮丧。这怎么可能?这甚至可能吗?我的意思是,这是同一个查询、同一个表、所有列的类型都相同,但其中一个在数据读取器中获得了不同的 C# 类型。这怎么会发生?
编辑 -> 我正在运行测试。即使我只从单个表中选择那一列,没有 JOIN 或 UNION 或 WHERE 或任何东西,问题也会发生。尝试删除列并重新创建它;同样的问题。尝试使用相同的列创建表的副本,问题不断发生。尝试使用较少的列创建表的副本,问题停止发生。这是否可能仅因为表中行为不端的列的列数/位置而发生?(该表有 77 列,有问题的列是表中的最后一列)。
是的,我们遇到了同样的问题 - ODP.NET 将输出 NUMBER 声明替换为 OracleDecimal,即使对于没有小数的值也是如此。
在我看来,如果类型被声明为 NUMBER 或像 Oracle 查询那样解释,即使值没有分数,它也会变成十进制。这与 NUMBER(N,0) 定义相反,其中 ODP.NET 实际上根据位数计算出 .NET 类型,并选择最小的有符号整数或浮点/小数类型来表示它。
如果您提供了实际的代码示例,它会有所帮助,但对于任何 ExecuteDataReader() 调用,以下代码应该是安全的:
using(var reader = cmd.ExecuteReader())
{
while(reader.Read())
{
var v = Convert.ToInt16(reader["name"]);
}
}
Run Code Online (Sandbox Code Playgroud)
输出参数的问题会变得更糟,因为 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();
Run Code Online (Sandbox Code Playgroud)
所以它可能仍然会失败,但至少你会知道,由于某种未知的原因,该规模是积极的。这种模式在多个地方重复,一般来说,所有 NUMBER 类型都由 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
Run Code Online (Sandbox Code Playgroud)
以下作品:
var v = Convert.ToInt16(reader["c2"]);
Run Code Online (Sandbox Code Playgroud)
而以下失败:
var v = reader.GetInt16(1); // InvalidCastException
Run Code Online (Sandbox Code Playgroud)
我发现的另一个解决方法是在定义输出或返回参数时,使用 OracleDbTypeEx 会有所帮助。
例如,如果 oracle 输出参数创建为
var p = new OracleParameter("name",OracleDbType.Int32, ParameterDirection.Output)
Run Code Online (Sandbox Code Playgroud)
它以 OracleDecimal 形式返回并导致前面描述的问题,但是当这样声明时:
var p = new OracleParameter("name",OracleDbType.Int32, ParameterDirection.Output);
p.OracleDbCodeEx = OracleDbType.Int32;
Run Code Online (Sandbox Code Playgroud)
它工作正常,值类型实际上是 .net 类型,并且是 System.Int32。
总体而言,ODP.NET 的实现相当糟糕,包括一些方法重载的选择。对于另一个示例,OracleDataReader.GetOrdinal() 被实现为对列名称的循环。该库的用户必须非常小心并特别注意功能测试。
归档时间: |
|
查看次数: |
3296 次 |
最近记录: |