C# 从Oracle.DataAccess.dll引发的NullReferenceException

C# 从Oracle.DataAccess.dll引发的NullReferenceException,c#,oracle,nullreferenceexception,odp.net,user-defined-types,C#,Oracle,Nullreferenceexception,Odp.net,User Defined Types,我在将Oracle数据提供程序与.NET一起使用时遇到问题。我使用用户定义对象的数组作为存储过程的IN参数。我已经将数据库模式添加到Visual Studio 2015 Server Explorer中,并生成了与我正在使用的UDT对应的自定义类型类。我正在使用以下代码调用该过程 OracleCommand cmd = DataBase.Connection.CreateCommand(); cmd.CommandText = "MYPROCEDURE"; cmd.CommandType = C

我在将Oracle数据提供程序与.NET一起使用时遇到问题。我使用用户定义对象的数组作为存储过程的IN参数。我已经将数据库模式添加到Visual Studio 2015 Server Explorer中,并生成了与我正在使用的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
    
    FUNCTION test(obj in MY_TYPE) RETURN NUMBER IS
    BEGIN
      RETURN obj.id*obj.value;
    END;
    
  • 该过程采用自定义集合类型,定义如下:

     CREATE OR REPLACE TYPE my_type AS OBJECT
     (
      id NUMBER;
      value NUMBER;
     )
    
    CREATE OR REPLACE my_type_varray AS VARRAY(50) OF my_type
    
    FUNCTION test(obj in MY_TYPE) RETURN NUMBER IS
    BEGIN
      RETURN obj.id*obj.value;
    END;
    
  • 参数的设置是唯一可接受的设置,我已尝试为集合创建自定义类型,但它会生成一个错误,显示“调用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的一个实例。该程序定义如下:

 CREATE OR REPLACE TYPE my_type AS OBJECT
 (
  id NUMBER;
  value NUMBER;
 )
CREATE OR REPLACE my_type_varray AS VARRAY(50) OF my_type
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。

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

因此,您必须传递两个参数,一个是
id
数组,另一个是
value
数组。然后可以在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;
在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:

您的代码哪一行出现故障?我知道异常在Oracle程序集中,但了解是否已到达
ExecuteOnQuery
将非常有用。异常是从ExecuteOnQuery中引发的。请将堆栈跟踪添加到post中。此外,如果您可以添加是否有任何其他使用UDT的调用,这将非常有用。(这个UDT在其他地方可以工作吗?你有不同的UDT可以工作吗?)我没有其他使用同一个UDT的工作调用,但是你有其他UDT吗?如果是这个UDT导致了问题,您是否尝试过在更简单的场景中使用它(例如,没有阵列)?UDT中有什么,您是否尝试过通过使UDT变得越来越简单来诊断这个问题?我尝试使用Java调用这个过程,它工作正常。即使它使用varray。如果你的意思是我不能在C#中使用varray,那么这就更有意义了。如果您可以发布一些调用存储过程的代码,将UDT的关联数组作为参数,我将非常感谢。您可以传递UDT的一个值或简单数据类型列表(例如NUMBER、VARCHAR2等),这些数据类型必须在PL/SQL中声明为关联数组。但是您不能传递UDT值的列表,我已经使用Java成功地传递了一个UDT变量。因此,我假设传递UDT的varray的限制不是来自数据库本身,而是来自ODP。但是,有一个名为Array的OracleDbType。它一定有一些用处。我已经阅读了Oracle的相关文档,没有发现任何暗示我无法通过UDT的varray的内容。我发现了这个
OracleDbType.Array
在早期版本中不可用,看起来Oracle添加了此类型。所以,我的声明“只能使用关联数组”同时似乎是错误的。但是,我假设说“您可以传递一个UDT值或一个简单数据类型列表”仍然有效