从Oracle.DataAccess.dll中抛出NullReferenceException

时间:2018-11-07 07:28:25

标签: c# oracle nullreferenceexception odp.net user-defined-types

在将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();

问题在于它会从Oracle驱动程序中抛出一个NullReferenceException,特别是从Oracle.DataAccess.Types.OracleUdt.SetValue(OracleConnection con, IntPtr pUdt, Int32 attrIndex, Object value, Object statusArray)中抛出。

注意:

  • 该连接已通过其他过程调用进行了测试,并且工作正常。
  • 我已通过调试器确保数组元素不为null且其“ IsNull”属性设置为false,并且其成员也不为null且每个成员均为“ 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,这意味着它接受了参数并继续进行处理。

  • 为简单起见,我省略了很多代码,并且手动编写了代码。但是如果需要,我会发布它。

其他信息:
stacktrace:

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。

1 个答案:

答案 0 :(得分:0)

据我所知,您不能使用VARRAY作为参数。唯一受支持的集合类型是关联数组(按表索引),例如

TYPE TArrayOfNumber IS TABLE OF NUMBER INDEX BY INTEGER;

因此,您将必须传递两个参数,一个为id的数组,一个为value的数组。然后您可以在PL / SQL中创建OBJECT类型

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;

在C#中是这样的:

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: