如何将Dapper micro ORM与Oracle一起使用来映射NUMBER(OracleDecimal)



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

我有一个Oracle存储过程,它使用REF CURSOR输出参数来返回3列记录。从根本上讲,这3个都是NUMBER(某种东西),但ODP.NET Oracle托管的提供程序似乎决定了将它们转换为哪种ODP.NET或.NET类型。

Dapper的Query()将此存储过程中的记录映射到POCO时,我遇到了问题。这一次,也许这实际上不是我的错——当一列显示为ODP.NET类型而不是.NET类型时,Dapper似乎会失败。如果我在我的POCO中评论一个冒犯性的专栏,一切都会好起来。

这里有两行来说明:

--------------------------------------------------------------------
RDWY_LINK_ID           RLC_LINK_OSET          SIGN                   
---------------------- ---------------------- ---------------------- 
1829                   1.51639964279667746989761971196153763602 1 
14380                  578.483600357203322530102380288038462364 -1 

在.NET中,第一列显示为int,第二列显示为类型OracleDecimal;第三列显示为decimal

例如,暂时删除Dapper并使用普通的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

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

有什么关于如何修补Dapper的建议吗?非常感谢。

更新:

经过思考,I RTF Med: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,它的Value属性将给您一个溢出异常,您唯一的方法是使用OracleDecimal的ToXxx()方法之一将其强制转换为较小的类型。

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

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

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

CAST(COLUMN AS BINARY_DOUBLE)

TO_BINARY_DOUBLE(COLUMN)

在这里列出的Oracle类型中,它被描述为:

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

Oracle使用的大多数其他数字类型最大为22字节,因此这与.NET 一样好

最新更新