如何在 Oracle 中使用 Dapper micro-ORM 来映射 NUMBER (OracleDecimal)

MC5*_*MC5 5 dapper odp.net-managed

如果列类型为 NUMBER(x,y),则 ODP.NET 提供程序会在 IDataReader.GetValue()/GetValues() 中引发异常,这样它将溢出所有 .NET 数字类型。因此 Dapper 无法将这样的列映射到 POCO 属性。

我有一个 Oracle 存储过程,它使用 REF CURSOR 输出参数返回 3 列记录。基本上所有 3 都是 NUMBER(something),但ODP.NET Oracle 托管提供程序似乎决定将它们变成 ODP.NET 或 .NET 类型。

我在 Dapper 的 Query() 将记录从这个 sproc 映射到 POCO 时遇到了问题。也许这实际上不是我的错,这一次 - 似乎当一列作为 ODP.NET 类型而不是 .NET 类型出现时,Dapper 失败了。如果我从我的 POCO 中评论一个有问题的专栏,一切正常。

这是一对行来说明:

--------------------------------------------------------------------
RDWY_LINK_ID           RLC_LINK_OSET          SIGN                   
---------------------- ---------------------- ---------------------- 
1829                   1.51639964279667746989761971196153763602 1 
14380                  578.483600357203322530102380288038462364 -1 
Run Code Online (Sandbox Code Playgroud)

第一列在 .NET 中被视为int,第二列作为OracleDecimal类型,第三列作为十进制。第二个是问题。

例如,暂时删除 Dapper 并使用 vanilla ODP.NET 访问这些记录,从而表明存在问题:

int linkid = (int)reader.GetValue(0);
decimal linksign = (decimal)reader.GetValue(2);
//decimal dlinkoffset = (decimal)reader.GetValue(1); //**invalid cast exception at at Oracle.ManagedDataAccess.Client.OracleDataReader.GetDecimal(Int32 i)**
//object olinkoffset = reader.GetValue(1); //**same**
//decimal dlinkoffset = reader.GetDecimal(1); //**same**
//object[] values = new object[reader.FieldCount];
//reader.GetValues(values); //**same**
OracleDecimal linkoffset = (OracleDecimal)reader.GetProviderSpecificValue(1); //this works!
double dblinkoffset = reader.GetDouble(1); //interesting, this works too!
//decimal dlinkoffset = linkoffset.Value; //overflow exception
dblinkoffset = linkoffset.ToDouble(); //voila
Run Code Online (Sandbox Code Playgroud)

我在 Dapper 的 SqlMapper.cs 文件中所做的一点浏览和断点表明它正在使用 GetValue()/GetValues() 从读取器中提取数据,如上所述,但失败了。

任何关于如何修补 Dapper 的建议?非常感谢。

更新:

经过反思,我 RTFMed:Oracle Data Provider for .NET Developer's Guide 的第 3 节“从 OracleDataReader 对象获取数据”,其中进行了解释。对于 NUMBER 列,ODP.NET 的 OracleDataReader 将尝试从 Byte 到 Decimal 的 .NET 类型序列以防止溢出。但是 NUMBER 仍可能溢出 Decimal,如果您尝试任何读取器的 .NET 类型访问器 (GetValue()/GetValues()),则会给出无效的强制转换异常,在这种情况下,您必须使用读取器的 ODP.NET 类型访问器 GetProviderSpecificValue( ),它给你一个 OracleDecimal,如果它溢出一个 Decimal,它的 Value 属性会给你一个溢出异常,你唯一的办法是使用 OracleDecimal 的 ToXxx() 方法之一将它强制转换为较小的类型。

但当然 ODP.NET 类型访问器不是 Dapper 用来保存读取器对象的 IDataReader 接口的一部分,因此当列类型将溢出所有 .NET 类型时,Dapper 本身似乎与 Oracle 不兼容。

问题仍然存在 - 聪明的人是否知道如何扩展 Dapper 来处理这个问题。在我看来,我需要一个扩展点,我可以在其中提供有关如何使用读取器的实现(强制它使用 GetDouble() 而不是 GetValue(),或强制转换为 OracleDataReader 并调用 GetProviderSpecificValue())用于某些 POCO 属性或列类型。

小智 1

为了避免这个问题,我使用了:

CAST(COLUMN AS BINARY_DOUBLE)

或者 TO_BINARY_DOUBLE(COLUMN)

此处列出的 Oracle 类型中的描述如下:

64 位浮点数。该数据类型需要 9 个字节,包括长度字节。

Oracle 使用的大多数其他数字类型最大为 22 字节,因此这对于 .NET 来说已经是最好的了