【问题标题】:Why do I get "Invalid Parameter binding" when using Oracle stored procedure with Udt and C#为什么在使用带有 Udt 和 C# 的 Oracle 存储过程时出现“无效的参数绑定”
【发布时间】:2018-08-30 10:40:07
【问题描述】:

我正在尝试在 Oracle DB 上调用一个简单的存储过程,该过程将 Udt(用户定义类型)作为输入并产生一个简单的 varchar2 输出。

但是我的代码显然有问题,因为我不断收到以下错误:

Invalid parameter binding
   at Oracle.DataAccess.Client.OracleParameter.PreBind_OracleObject(OracleConnection conn)
   at Oracle.DataAccess.Client.OracleParameter.PreBind_Object(OracleConnection conn)
   at Oracle.DataAccess.Client.OracleParameter.PreBind(OracleConnection conn, IntPtr errCtx, Int32 arraySize)
   at Oracle.DataAccess.Client.OracleCommand.ExecuteReader(Boolean requery, Boolean fillRequest, CommandBehavior behavior)
   at Oracle.DataAccess.Client.OracleCommand.ExecuteReader()
   at Oracle.DataAccess.Client.OracleCommand.ExecuteScalar()

我一直在阅读 Oracle 文档
https://docs.oracle.com/database/121/ODPNT/extenRest.htm#ODPNT453
https://docs.oracle.com/cd/E14435_01/win.111/e10927/featUDTs.htm#CJAHJHGE
https://docs.oracle.com/cd/E14435_01/win.111/e10927/IOracleCustomTypeInterface.htm#BABHGCIG 并在谷歌上搜索各种来源:
https://www.codeproject.com/Articles/141728/Interaction-between-C-Application-and-Oracle-throu
Invalid parameter name binding oracle UDT
C# Call Oracle Stored Procedure with nested UDT
寻找可能出错但没有运气的线索。

我的存储过程有以下声明:

PROCEDURE Test2(TEST2_IN_o IN TEST2_IN, errortext_out OUT varchar2); 

然后输入:

create or replace TYPE TEST2_IN AS OBJECT (
    FIELD1 VARCHAR2 (50 Byte),      
    FIELD2 VARCHAR2 (50 Byte),     
    FIELD3 VARCHAR2 (20 Byte),   
    USER_ID VARCHAR2(20)           
) NOT FINAL

这是我的对象映射:

namespace OracleProcedureTest
{
    [OracleCustomTypeMapping("TEST2_IN")]
    public class TEST2_IN : IOracleCustomType, INullable
    {
        public TEST2_IN(string Field1, string Field2, string Field3, string UserID)
        {
            FIELD1 = Field1;
            FIELD2 = Field2;
            FIELD3 = Field3;
            USER_ID = UserID;
        }

        [OracleObjectMapping("FIELD1")]
        public string FIELD1 { get; set; }

        [OracleObjectMapping("FIELD2")]
        public string FIELD2 { get; set; }

        [OracleObjectMapping("FIELD3")]
        public string FIELD3 { get; set; }

        [OracleObjectMapping("USER_ID")]
        public string USER_ID { get; set; }

        public bool IsNull { get; set; }

        public static TEST2_IN Null => new TEST2_IN { IsNull = true };

        public void FromCustomObject(OracleConnection con, IntPtr pUdt)
        {
            OracleUdt.SetValue(con, pUdt, "FIELD1", FIELD1);
            OracleUdt.SetValue(con, pUdt, "FIELD2", FIELD2);
            OracleUdt.SetValue(con, pUdt, "FIELD3", FIELD3);
            OracleUdt.SetValue(con, pUdt, "USER_ID", USER_ID);
        }

        public void ToCustomObject(OracleConnection con, IntPtr pUdt)
        {
            FIELD1 = ((string)(OracleUdt.GetValue(con, pUdt, "FIELD1")));
            FIELD2 = ((string)(OracleUdt.GetValue(con, pUdt, "FIELD2")));
            FIELD3 = ((string)(OracleUdt.GetValue(con, pUdt, "FIELD3")));
            USER_ID = ((string)(OracleUdt.GetValue(con, pUdt, "USER_ID")));
        }
    }
}

这是调用的代码:

public static void RunTestFunction2(OracleConnection connection)
        {
            OracleTransaction transaction = null;
            OracleCommand cmd = null;

            //Start transaction
            transaction = connection.BeginTransaction(IsolationLevel.ReadCommitted);

            //Create the command
            cmd = new OracleCommand();
            cmd.Connection = connection;
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.CommandText = "MyPackage.Test2";

            //Create the input object
            TEST2_IN inputObject = new TEST2_IN("Hello", "world", "OracleDB ", "123456");

            //Create the input parameter
            OracleParameter parameter_in = cmd.CreateParameter();
            parameter_in.OracleDbType = OracleDbType.Object;
            parameter_in.Direction = ParameterDirection.Input;
            parameter_in.ParameterName = "TEST2_IN_o";
            parameter_in.UdtTypeName = "TEST2_IN";
            parameter_in.Value = inputObject;
            cmd.Parameters.Add(parameter_in);

            //Create output parameter
            OracleParameter parameter_out = cmd.Parameters.Add("errortext_out", OracleDbType.Varchar2, 200);
            parameter_out.Direction = ParameterDirection.Output;

            //Run command and print result.
            Oracle.DataAccess.Types.OracleString oString;

            cmd.ExecuteNonQuery();
            oString = (Oracle.DataAccess.Types.OracleString)parameter_out.Value;

            Console.WriteLine("Return message was '" + oString.Value + "'.");
        }

感谢任何帮助。

我知道用于 Visual Studio 的 Oracle 工具允许从服务器资源管理器创建这些对象,但我无法让它工作。我安装了开发人员工具,但我只能看到不允许创建 Udt 类的托管驱动程序。

【问题讨论】:

  • 您为什么使用ExecuteScalar,这仅适用于诸如 Min、Max、Average 之类的标量操作以及更多的过程执行,您应将 ExecuteNonQuery 用于 DML,将 ExecuteReader 用于数据获取操作。还有为什么UDT的参数没有在任何地方指定它的名字,应该是TEST2_IN_o
  • 我将代码更改为使用“ExecuteScalar”并添加了参数名称,但问题仍然存在。
  • 请编辑您的代码以显示您在做什么,我提到不使用ExecuteScalar
  • 按要求更新

标签: c# oracle stored-procedures user-defined-types


【解决方案1】:

发现了问题,并且在我的键盘和屏幕之间发现了很多次:

我的解决方案中有一个名为 TEST2_IN 的结构。计划是使用结构和类进行测试。然而,oracle 的反射编程将该类型作为要使用的类型而不是我上面介绍的类,即使我没有为该结构分配任何属性。因此,在删除结构后,上面的代码按计划工作!

【讨论】:

猜你喜欢
  • 2011-05-15
  • 2017-08-26
  • 2016-01-25
  • 2017-05-25
  • 1970-01-01
  • 2013-11-17
  • 2013-11-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多