简体   繁体   中英

Calling Oracle function that returns type from C#

I am using c# to try and call a function in an Oracle package that returns a Type.

I've spent the last couple of days researching this, so far the advice i've come across is:

  • to use the odp.net 11g Data Access driver.
  • ensure the parameter output direction is set to returnvalue
  • ensure the parameter output is first parameter added
  • Give the output parameter a udttypename which is the Oracle type name.
  • To make sure this udttypename is Upper Case (A few similar cases asked on her were resolved by this)

Below is the Oracle package (package is called prefs):

Type P_Details Is Record(
   var1    a.a_Type_Key%Type
  ,var2    Varchar2(1)
  ,var3    a.b%Type
  ,var4    c.Type_Key%Type
  ,var5    d.Code%Type
  ,var6    d.Product_Path%Type
  ,var7    a.Channel_Key%Type
  ,var8    a.From_Date%Type
  ,var9    a.To_Date%Type);

Type P_List Is Table Details;

Function Get(p_1   In     Number,
             p_2   In     Varchar2,
             p_3   In     Varchar2,
             p_4   In     Date,
             p_5   In Out Varchar2) Return List;

Below is the C# code used for calling the Oracle package.

using (var connection = new OracleConnection(ConnectionString))
{
    using (var command = new OracleCommand
                             {
                                 CommandType = CommandType.StoredProcedure,
                                 CommandText = "PACKAGENAME.FUNCTIONNAME",
                                 Connection = connection,
                                 BindByName = true
                             })
    {
        var output = new OracleParameter
                         {
                             UdtTypeName = "PREFS.PREFERENCE_LIST",
                             ParameterName = "p_details",
                             OracleDbType = OracleDbType.Object,
                             Direction = ParameterDirection.ReturnValue
                         };
        command.Parameters.Add(output);
        command.Parameters.Add(new OracleParameter
                                   {
                                       ParameterName = "p_1",
                                       OracleDbType = OracleDbType.Decimal,
                                       Direction = ParameterDirection.Input,
                                       Value = details.RuleId
                                   });
        command.Parameters.Add(new OracleParameter
                                   {
                                       ParameterName = "p_2",
                                       OracleDbType = OracleDbType.Decimal,
                                       Direction = ParameterDirection.Input,
                                       Value = details.CustomerDetails.CtiId
                                   });
        command.Parameters.Add(new OracleParameter
                                   {
                                       ParameterName = "p_3",
                                       OracleDbType = OracleDbType.Varchar2,
                                       Direction = ParameterDirection.Input,
                                       Value = details.CustomerDetails.Surname
                                   });
        command.Parameters.Add(new OracleParameter
                                   {
                                       ParameterName = "p_4",
                                       OracleDbType = OracleDbType.Varchar2,
                                       Direction = ParameterDirection.Input,
                                       Value = details.CustomerDetails.Postcode
                                   });
        command.Parameters.Add(new OracleParameter
                                   {
                                       ParameterName = "p_5",
                                       OracleDbType = OracleDbType.Date,
                                       Direction = ParameterDirection.Input,
                                       Value = details.CustomerDetails.DateOfBirth
                                   });
        command.Parameters.Add(new OracleParameter
                                   {
                                       ParameterName = "p_6",
                                       OracleDbType = OracleDbType.Varchar2,
                                       Direction = ParameterDirection.InputOutput
                                   });
        connection.Open();
        command.ExecuteNonQuery();
    }
}

I'm now getting an error

"OCI-22303: type "PACKAGENAME.TYPENAME" not found"

For the UDTTYPENAME i've tried the following formats

  • TYPENAME
  • FUNCTIONNAME.TYPENAME
  • PACKAGENAME.TYPENAME
  • PACKAGENAME.TYPENAME
  • PACKAGENAME.FUNCTIONNAME.TYPENAME
  • SCHEMA.PACKAGENAME.TYPENAME

I'd appreciate any help and response for this as i've now run out of ideas.

You can actually simplify the casting and preparing parameters for all your Routines(Procedures,functions etc.) and also parameters of ref cursor type with this little automation

a) define a following type and routine in a common package (lets call it utils).

Type recRoutineSchema is Record (ColumnName varchar2(64),DataType Varchar2(20), ColumnOrder number, Direction varchar2(10), sSize nUMBER);

Type tblRoutineSchema is table of recRoutineSchema;

  function ftRoutineSchema(pkg varchar2,Routine varchar2) return  tblRoutineSchema  PIPELINED is 
      x recRoutineSchema;
       pkN  varchar2(100);
   rtN  varchar2(100);
   Begin 
     FOR Y in ( Select Argument_Name  ColumnName
                      ,Data_type      DataType
                      ,Position       ColumnOrder
                      ,In_out         Direction
                      ,Data_length    SSize 
                   from   user_ARGUMENTS 
                      where  package_Name=Upper(pkg) 
                         and object_name=Upper(Routine) order by position 
                         ) 
     LOOP
       PIPE ROW(Y);
     END LOOP;
     Return;  
   End;

b) and ac# method to call above function to retrieve and setup parameters of the procedure/function you are calling

public void SetupParams(string RoutineName, OracleCommand cmd, IDictionary<string, string> prms, bool keepConnectionOpen = true)
            {
            Debug.WriteLine("Setting parameters for " + RoutineName);
            if (cmd != null) cmd.Parameters.Clear();
            string pname = "";
            string[] s = RoutineName.Split('.');
            DataTable tblParams = Select(String.Format("Select * from Table(pkgUtils.ftRoutineSchema('{0}','{1}')) ", s[0], s[1]));
           cmd.CommandText=RoutineName; 
           foreach (DataRow dr in tblParams.Rows)
                {
                using (OracleParameter p = new OracleParameter())
                    {
                    pname = dr["COLUMnNAME"].ToString() == "" ? "returnvalue" : pname = dr["COLUMnNAME"].ToString().ToLower();
                    if (prms.Keys.Contains(pname)) p.Value = prms[pname];
                    string direction = dr["Direction"].ToString().ToLower();
                    string sptype = (string)dr["DataType"];
                    string[] sx = dr["DataType"].ToString().Split(new char[] { '(', ',', ')' });
                    direction = pname == "returnvalue" ? "rc" : direction;
                    p.ParameterName = pname;
                    #region case type switch
                    switch (sx[0].ToLower())
                        {
                        case "number":
                            // p.DbType = OracleDbType.Decimal;
                            p.OracleDbType = OracleDbType.Decimal;
                            break;

                        case "varchar2":
                            p.DbType = DbType.String;
                            p.Size = 65536;
                            //  p.Size = prms[pname].Length;
                            // p.Size = int.Parse(sx[1]);
                            break;
                        case "ref cursor":
                            p.OracleDbType = OracleDbType.RefCursor;
                            // direction = "rc"; // force return value

                            break;
                        case "datetime":
                            p.DbType = DbType.DateTime;
                            break;
                        case "ntext":
                        case "text":
                            p.DbType = DbType.String;
                            p.Size = 65536;
                            break;
                        default:
                            break;
                        }
                    //-------------------------------------------------------------------------------
                    switch (direction)
                        {
                        case "in": p.Direction = ParameterDirection.Input; break;
                        case "out": p.Direction = ParameterDirection.Output; break;
                        case "in/out": p.Direction = ParameterDirection.InputOutput; break;
                        case "rc": p.Direction = ParameterDirection.ReturnValue; break;
                        default: break;
                        }

                    #endregion
                    cmd.Parameters.Add(p); ;
                    }
                }
            } 

c). now you can easily call any function/proc as follows this procedure actually returns two out refcursor parameters to poplulate a dataset.

 private void btnDumpExcel_Click(object sender, EventArgs e)
         {
         IDictionary<string, string> p = new Dictionary<string, string>();

         p.Add("pcomno", "020");
         p.Add("pcpls", "221");
         p.Add("pUploaderName", "Anthony Peiris");
         try
            {
            pGroupDs = O.execProc2DatSet("priceWorx.prSnapshotDiscounts", p, false, false);
            Excel.MakeWorkBook(ref pGroupDs, ref O, "1");

            }
         catch (Exception ex)
            {
            Debug.WriteLine(ex);
            Debugger.Break();
            }
         //Excel.MakeWorkBook(ref ds, ref O, "1");

         }

Here is the method O.execProc2DataSet

public DataSet execProc2DatSet(string storedProcedureName, IDictionary<string, string> prms, bool propagateDbInfo, bool leaveConnectionOpen = false)
            {
           // initPackage(storedProcedureName.Substring(0,storedProcedureName.IndexOf('.')));
            try
                {
                using (OracleCommand cmd = new OracleCommand("", conn))
                    {
                    cmd.CommandType = CommandType.StoredProcedure;
                    cmd.CommandText = storedProcedureName;
                    //dep = new OracleDependency(cmd);
                    //dep.OnChange += new OnChangeEventHandler(dep_OnChange);
                    if (prms != null) SetupParams(storedProcedureName, cmd, prms, true);
                    using (OracleDataAdapter da = new OracleDataAdapter(cmd))
                        {
                        if (conn.State != ConnectionState.Open)
                            {
                            conn.Open();
                            cmd.Connection = conn;
                            }
                        using (DataSet ds = new DataSet())
                            {
                            da.Fill(ds);
                            return ds;
                            }
                        }
                    }
                }
            catch (Exception ex)
                {
                Debug.WriteLine(ex);
                Debugger.Break();
                return null;
                }
            finally
                {
                if (!leaveConnectionOpen) conn.Close();
                }
            }

This approach allows you to change your Proce/function parameters without being concerend about what parameters may have changed since last, since the parameter setup is now fully automatic. HTH

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM