Execute an oracle Function that returns a reference cursor in C#

假装没事ソ 提交于 2019-12-19 13:44:12

问题


I have an oracle package with a procedure that has a in out reference cursor. My understanding is that this is pretty standard.

What I didn't like is the fact that I had to write a ton of code to just see the output. So I asked this question and it turns out I can get what I want by creating a function that wraps the procedure.

Update: Looks like I don't need the function anymore but it may be worth knowing anyway for those curious see the original question and answer updates.

Here's the function

FUNCTION GetQuestionsForPrint (user in varchar2)
  RETURN MYPACKAGE.refcur_question
AS  

    OUTPUT MYPACKAGE.refcur_question;

BEGIN 

      MYPACKAGE.GETQUESTIONS(p_OUTPUT => OUTPUT, 
      p_USER=> USER ) ;


  RETURN OUTPUT;
END;

and here's what I do to execute it in SQL Developer

var r refcursor;
exec :r := mypackage.getquestionsForPrint('OMG Ponies');
print r;

So from now on I'm probably going to add the ForPrint functions to all my procedures.

This got me thinking, maybe functions are what I want and I don't need procedures.

To test this I tried executing the function from .NET, except I can't do it. Is this really the way it is.

using (OracleConnection cnn = new OracleConnection("Data Source=Test;User Id=Test;Password=Test;"))
{
    cnn.Open();
    OracleCommand cmd = new OracleCommand("mypackage.getquestionsForPrint");
    cmd.CommandType = System.Data.CommandType.StoredProcedure;

    cmd.Parameters.Add ( "p_USER", "OMG Ponies");

    cmd.Connection = cnn;
    OracleDataReader rdr = cmd.ExecuteReader();

    while (rdr.Read())
    {
        Console.WriteLine(rdr.GetOracleValue(0));
    }

    Console.ReadLine();
}

So I get the error.

getquestionsForPrint is not a procedure or is undefined

I tried ExecuteScalar as well with the same result.

EDIT Taking Slider345's advice I've also tried setting the command type to text and using the following statement and I get invalid SQL statement

mypackage.getquestionsForPrint('OMG Poinies');

and

var r refcursor; exec :r :=  mypackage.getquestionsForPrint('OMG Poinies'); 

Using Abhi's variation for the command text

select mypackage.getquestionsForPrint('OMG Poinies') from dual

resulted in

The instruction at "0x61c4aca5" referenced memory at "0x00000ce1". The memory could not be "read".

Am I just barking up the wrong tree?

Update Attempting to add an output parameter doesn't help.

cmd.Parameters.Add(null, OracleDbType.RefCursor, ParameterDirection.Output);

Not sure what the name should be since its the return value of a function (I've tried null, empty string, mypackage.getquestionsForPrint) but in all cases it just results in

ORA-06550: line 1, column 7: PLS-00306: wrong number or types of arguments in call to 'getquestionsForPrint'

Final Edit (hopefully)

Apparently Guddie asked a similar question 3 months after I did. He got the answer which is to

  • Set your command text to an anonymous block
  • Bind a parameter to the ref cursor setting the direction to output
  • Call Execute non reader.
  • Then use your parameter

using (OracleConnection cnn = new OracleConnection("Data Source=Test;User Id=Test;Password=Test;"))
{
    cnn.Open();
    OracleCommand cmd = new OracleCommand("mypackage.getquestionsForPrint");
    cmd.CommandType = CommandType.Text;

    cmd.CommandText = "begin " +
              "    :refcursor1 := mypackage.getquestionsForPrint('OMG Ponies') ;"  +
              "end;";

    cmd.Connection = cnn;
    OracleDataAdapter da = new OracleDataAdapter(cmd);
    cmd.ExecuteNonQuery();

    Oracle.DataAccess.Types.OracleRefCursor t = (Oracle.DataAccess.Types.OracleRefCursor)cmd.Parameters[0].Value;
    OracleDataReader rdr = t.GetDataReader();
    while(rdr.Read())
        Console.WriteLine(rdr.GetOracleValue(0));

    Console.ReadLine();
}

回答1:


I have not tested this with a function, but for my stored procedures. I specify the out parameter for the refCursor.

command.Parameters.Add(new OracleParameter("refcur_questions", OracleDbType.RefCursor, ParameterDirection.Output));

If you are able to get the function to work with the CommandType.Text. I wonder if you can try adding the parameter above except with the direction as:

ParameterDirection.ReturnValue

I am using Oracle.DataAccess version 2.111.6.0




回答2:


I had to go up and down between the question and answers to figure out the full code that works. So I am giving the full code here that worked for me for others -

var sql = @"BEGIN :refcursor1 := mypackage.myfunction(:param1) ; end;";
using(OracleConnection con = new OracleConnection("<connection string>"))
using(OracleCommand com = new OracleCommand())
{
     com.Connection = con;
     con.Open();
     com.Parameters.Add(":refcursor1", OracleDbType.RefCursor, ParameterDirection.Output);
     com.Parameters.Add(":param1", "param");
     com.CommandText = sql;
     com.CommandType = CommandType.Text;
     com.ExecuteNonQuery();
     OracleRefCursor curr = (OracleRefCursor)com.Parameters[0].Value;
     using(OracleDataReader dr = curr.GetDataReader())
     {
         if(dr.Read())
         {
             var value1 = dr.GetString(0);
             var value2 = dr.GetString(1);
         }
     }
 }

Hope it helps.




回答3:


My guess is that you cannot call a function with a Command object of type 'StoredProcedure'. Can you try a Command object of type 'Text' and exec the function within the Command Text?




回答4:


I know this is quite an old post, but since it took me so long to figure out all of the minutia involved in getting .NET to "fight nice" with Oracle, I figured I'd put this advice out there for anyone else in this sticky situation.

I frequently call Oracle stored procedures that return a REF_CURSOR in our environment (.NET 3.5 against Oracle 11g). For a function, you can indeed name the parameter anything you'd like, but then you need to set its System.Data.ParameterDirection = ParameterDirection.ReturnValue then ExecuteNonQuery against the OracleCommand object. At that point the value of that parameter will be the ref_cursor that the Oracle function returned. Just cast the value as an OracleDataReader and loop through the OracleDataReader.

I'd post the full code, but I wrote the data access layer in VB.NET years ago, and the bulk of the code consuming the data access layer (our corporate intranet) is in C#. I figured mixing languages in a single response would be the larger faux pas.



来源:https://stackoverflow.com/questions/3535405/execute-an-oracle-function-that-returns-a-reference-cursor-in-c-sharp

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!