NullReferenceException Выброшено из Oracle.DataAccess.dll

У меня проблема с использованием Oracle Data Provider с .NET. Я использую массив пользовательских объектов в качестве параметра IN для хранимой процедуры. Я добавил схему базы данных в обозреватель серверов Visual Studio 2015 и сгенерировал пользовательский класс типов, соответствующий UDT, который я использую. Я использую следующий код для вызова процедуры.

OracleCommand cmd = DataBase.Connection.CreateCommand();
cmd.CommandText = "MYPROCEDURE";
cmd.CommandType = CommandType.StoredProcedure;
cmd.BindByName = true;

MY_TYPE[] arr = new MY_TYPE[2];
arr[0] = new MY_TYPE(1, 2);
arr[1] = new MY_TYPE(3, 4);

OracleParameter pEntries = new OracleParameter();
pEntries.ParameterName = "ENTRIES";
pEntries.Direction = ParameterDirection.Input;
pEntries.OracleDbType = OracleDbType.Array;
pEntries.UdtTypeName = "MY_TYPE";
pEntries.Value = arr;
pEntries.Size = 2;

cmd.Parameters.Add(pEntries);
cmd.Connection.Open();
cmd.ExecuteNonQuery();

Проблема в том, что он выдает NullReferenceException из драйвера Oracle, в частности, из Oracle.DataAccess.Types.OracleUdt.SetValue(OracleConnection con, IntPtr pUdt, Int32 attrIndex, Object value, Object statusArray).

Заметки:

  • Соединение проверено с другими вызовами процедур и работает правильно.
  • С помощью отладчика я убедился, что элементы массива не являются нулевыми, а их свойство "IsNull" имеет значение false, и что их элементы не являются нулевыми, а свойство каждого члена "IsNull" имеет значение false.
  • UDT определяется следующим образом:

     CREATE OR REPLACE TYPE my_type AS OBJECT
     (
      id NUMBER;
      value NUMBER;
     )
    
  • Процедура принимает пользовательский тип коллекции, определенный следующим образом:

    CREATE OR REPLACE my_type_varray AS VARRAY(50) OF my_type
    
  • Настройки параметра являются единственными допустимыми настройками, я попытался создать собственный тип для коллекции, но он выдает ошибку, говорящую «неправильное число или типы аргументов при вызове MYPROCEDURE», эти настройки генерируют исключение NullReferenceException, что означает, что он принял параметры и перешли к их обработке.

  • Для простоты я опустил большую часть кода и написал его вручную. Но выложу, если надо.

Дополнительная информация:
трассировка стека:

Oracle.DataAccess.Types.OracleUdt.SetValue(OracleConnection con, IntPtr pUdt, Int32 attrIndex, Object value, Object statusArray)   
at Oracle.DataAccess.Types.OracleUdt.SetValue(OracleConnection con, IntPtr pUdt, Int32 attrIndex, Object value)   
at Oracle.DataAccess.Client.OracleParameter.SetUDTFromArray(OracleConnection conn, Object array, Int32 i)   
at Oracle.DataAccess.Client.OracleParameter.PreBind_Collection(OracleConnection conn)   
at Oracle.DataAccess.Client.OracleParameter.PreBind(OracleConnection conn, IntPtr errCtx, Int32 arraySize, Boolean bIsFromEF, Boolean bIsSelectStmt)   at Oracle.DataAccess.Client.OracleCommand.ExecuteNonQuery()

Я создал простую тестовую процедуру под названием TEST, которая использует один экземпляр UDT. Процедура определяется следующим образом:

FUNCTION test(obj in MY_TYPE) RETURN NUMBER IS
BEGIN
  RETURN obj.id*obj.value;
END;

Код для вызова процедуры:

OracleCommand cmd = DataBase.Connection.CreateCommand();
cmd.CommandText = "TEST";
cmd.CommandType = CommandType.StoredProcedure;
cmd.BindByName = true;

MY_TYPE obj = new MY_TYPE(2, 3);

OracleParameter pEntries = new OracleParameter();
Entries.ParameterName = "obj";
pEntries.Direction = ParameterDirection.Input;
pEntries.OracleDbType = OracleDbType.Object;
pEntries.UdtTypeName = "MY_TYPE";
pEntries.Value = obj;
cmd.Parameters.Add(pEntries);

// -- omitted some code for the return value parameter

cmd.Connection.Open();
cmd.ExecuteNonQuery();

Предыдущий код работал правильно, и результат был 6.


person Ali A. Ismaeel    schedule 07.11.2018    source источник
comment
В какой строке вашего кода происходит сбой? Я понимаю, что исключение находится в сборке Oracle, но было бы полезно знать, достигло ли оно ExecuteNonQuery или нет.   -  person Jon Skeet    schedule 07.11.2018
comment
Исключение создается из ExecuteNonQuery.   -  person Ali A. Ismaeel    schedule 07.11.2018
comment
Пожалуйста, добавьте трассировку стека в сообщение. Кроме того, если бы вы могли добавить, есть ли у вас какие-либо другие вызовы, работающие с UDT, это было бы полезно. (Работает ли этот UDT в другом месте? У вас есть другие работающие UDT?)   -  person Jon Skeet    schedule 07.11.2018
comment
У меня нет других рабочих вызовов с таким же udt   -  person Ali A. Ismaeel    schedule 07.11.2018
comment
У вас это работает с другими UDT? Если именно этот UDT вызывает проблемы, экспериментировали ли вы с его использованием в более простых сценариях (например, без массивов)? Что находится в UDT, и пытались ли вы диагностировать это, упрощая UDT?   -  person Jon Skeet    schedule 07.11.2018
comment
Я создал простую тестовую процедуру, которая использует один экземпляр UDT, и она работала правильно. Добавлю подробности в пост.   -  person Ali A. Ismaeel    schedule 07.11.2018


Ответы (1)


Насколько я знаю, вы не можете использовать VARRAY в качестве параметра. Единственным поддерживаемым типом коллекции являются ассоциативные массивы (индексные таблицы), например.

TYPE TArrayOfNumber IS TABLE OF NUMBER INDEX BY INTEGER;

Итак, вам нужно будет передать два параметра, один массив id и один массив для value. Затем вы можете создать тип OBJECT в PL/SQL.

PROCEDURE MYPROCEDURE(IdList IN TArrayOfNumber, ValueList IN TArrayOfNumber ) IS

   my_type_list my_type_varray;
BEGIN

   FOR i IN IdList.FIRST..IdList.LAST LOOP
      my_type_list(i) := my_type(IdList(i), ValueList(i));
   END LOOP;

...

END;

а в С# так:

var par1 = cmd.Parameters.Add("IdList ", OracleDbType.Int16, ParameterDirection.Input);
par1.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
par1.Value = new int[] { 1, 2};
par1.Size = 2:


var par2 = cmd.Parameters.Add("ValueList ", OracleDbType.Int16, ParameterDirection.Input);
par2.CollectionType = OracleCollectionType.PLSQLAssociativeArray;
par2.Value = new int[] { 3, 4};
par2.Size = 2:
person Wernfried Domscheit    schedule 07.11.2018
comment
Я попытался вызвать эту процедуру с помощью Java, и она работала правильно. Несмотря на то, что он использует varray. Если вы имеете в виду, что я не могу использовать varray в С#, то это имеет больше смысла. Если бы вы могли опубликовать код вызова хранимой процедуры, которая принимает ассоциативный массив UDT в качестве параметра, я был бы очень благодарен. - person Ali A. Ismaeel; 07.11.2018
comment
Вы можете передать одно значение UDT или список простых типов данных (например, NUMBER, VARCHAR2 и т. д.), которые должны быть объявлены как ассоциативный массив в PL/SQL. Но вы не можете передать список значений UDT. - person Wernfried Domscheit; 07.11.2018
comment
Я успешно прошел varray UDT с помощью Java. Поэтому я предполагаю, что ограничение на передачу varray UDT исходит не от самой базы данных, а от ODP. Однако существует тип OracleDbType с именем Array. Это должно иметь какую-то пользу. Я прочитал соответствующую документацию от Oracle и не нашел ничего, что подразумевало бы, что я не могу передать varray UDT. - person Ali A. Ismaeel; 07.11.2018
comment
Я нашел этот Тип перечисления OracleDbType. OracleDbType.Array не было доступно в более ранних выпусках, похоже, Oracle добавила этот тип. Итак, мое утверждение «Вы можете использовать только ассоциативный массив» пока кажется неверным. Тем не менее, я предполагаю, что по-прежнему можно сказать, что вы можете передать одно значение определяемого пользователем типа или список простых типов данных. - person Wernfried Domscheit; 07.11.2018