Как использовать 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, а третий - как десятичный. Вторая проблема.
Например, удаление 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
То, что я немного просмотрел и остановил в файле Daq SqlMapper.cs, показывает мне, что он извлекает данные из читателя с помощью GetValue () / GetValues (), как указано выше, что не удается.
Любые предложения, как исправить Dapper вверх? Большое спасибо.
ОБНОВИТЬ:
После размышления я RTFMed: Раздел 3, "Получение данных из объекта OracleDataReader" в Oracle Data Provider for .NET Developer Guide, который объясняет. Для столбцов NUMBER OracleDataReader в ODP.NET попытается использовать последовательность типов.NET от байтов до десятичных, чтобы предотвратить переполнение. Но NUMBER может по-прежнему переполнять десятичное число, давая недопустимое исключение приведения, если вы пытаетесь использовать любой из методов доступа.NET читателя (GetValue()/GetValues ()), и в этом случае вам нужно использовать метод доступа типа ODP.NET читателя GetProviderSpecificValue(), который дает вам OracleDecimal, и если он переполняет десятичное число, его свойство Value выдаст вам исключение переполнения, и единственным выходом для вас является принудительное приведение его к меньшему типу с помощью одного из методов OracleDecimal ToXxx().
Но, конечно, средство доступа к типу ODP.NET не является частью интерфейса IDataReader, используемого Dapper для хранения объектов чтения, поэтому кажется, что Dapper сам по себе несовместим с Oracle, когда тип столбца переполняет все типы.NET.
Остается вопрос - умные люди знают, как расширить Dapper, чтобы справиться с этим. Мне кажется, мне нужна точка расширения, где я мог бы обеспечить реализацию того, как использовать читателя (заставляя его использовать GetDouble() вместо GetValue(), или приводя к OracleDataReader и вызывая GetProviderSpecificValue()) для определенного свойства POCO или типы столбцов.
1 ответ
Чтобы избежать этой проблемы, я использовал:
CAST(COLUMN AS BINARY_DOUBLE)
или жеTO_BINARY_DOUBLE(COLUMN)
В перечисленных здесь типах Oracle это описывается так:
64-битное число с плавающей запятой. Этот тип данных требует 9 байтов, включая длину байта.
Большинство других типов чисел, используемых Oracle, имеют максимальный размер 22 байта, так что это так же хорошо, как и для.NET