【问题标题】:NullReferenceException Thrown from Oracle.DataAccess.dll从 Oracle.DataAccess.dll 引发 NullReferenceException
【发布时间】:2019-04-10 15:00:23
【问题描述】:

我在使用带有 .NET 的 Oracle 数据提供程序时遇到问题。我正在使用用户定义对象的数组作为存储过程的 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)

注意事项:

  • 连接已通过其他过程调用进行测试,并且工作正常。
  • 我已通过调试器确保数组元素不为空且它们的“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。

【问题讨论】:

  • your 代码的哪一行失败了?我知道异常发生在 Oracle 程序集中,但了解它是否已到达 ExecuteNonQuery 会很有用。
  • 异常是从 ExecuteNonQuery 中抛出的。
  • 请将堆栈跟踪添加到帖子中。此外,如果您可以添加是否有任何其他使用 UDT 的调用,那将很有用。 (此 UDT 是否在其他地方工作?你有不同的 UDT 工作吗?)
  • 我没有其他使用相同 udt 的工作调用
  • 你有这个与其他 UDT 一起使用的吗?如果是这个 UDT 引起了问题,您是否尝试过在更简单的场景中使用它(例如,没有数组)? UDT 中有什么,您是否尝试过通过使 UDT 更简单和更简单来进行诊断?

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


【解决方案1】:

据我所知,您不能使用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:

【讨论】:

  • 我尝试使用 Java 调用此过程并且它工作正常。即使它使用可变数组。如果您的意思是我不能在 C# 中使用 varray,那么这更有意义。如果您可以发布一些调用存储过程的代码,该存储过程将 UDT 的关联数组作为参数,我将非常感激。
  • 您可以传递一个UDT的一个值或必须在PL/中声明为关联数组的简单数据类型列表(例如NUMBER、VARCHAR2等) SQL。但是您不能传递 UDT 值列表。
  • 我已经使用 Java 成功传递了一系列 UDT。所以我认为传递 UDT 的 varray 的限制不是来自数据库本身,而是来自 ODP。但是,有一个称为 Array 的 OracleDbType。它一定有一些用处。我已阅读 Oracle 的相关文档,但没有发现任何暗示我无法传递 UDT 的 varray。
  • 我找到了这个OracleDbType Enumeration TypeOracleDbType.Array 在早期版本中不可用,看起来 Oracle 添加了这种类型。因此,我的声明“您只能使用关联数组”似乎同时是错误的。但是,我认为说“您可以传递 one 的 UDT 值或 simple 数据类型列表”仍然有效
猜你喜欢
  • 1970-01-01
  • 2014-03-31
  • 1970-01-01
  • 2010-10-24
  • 1970-01-01
  • 1970-01-01
  • 2012-11-16
  • 2012-09-15
  • 1970-01-01
相关资源
最近更新 更多