Using ODP.NET to get a RECORD from PL/SQL function, without touching PL/SQL code

若如初见. 提交于 2019-12-05 03:33:28

问题


The title is pretty-self-explanatory: from a C# application, using ODP.NET, I'm trying to call a PL/SQL function that returns not a simple value, but a record.

Unfortunately, I'm not authorized to add or change the PL/SQL code, so attempting to wrap the function in another function that returns a different type is not an option for me.

Here is a simplified example...

PL/SQL:

CREATE OR REPLACE PACKAGE FOO_PACKAGE AS

    TYPE FOO_RECORD IS RECORD (
        BAR VARCHAR2(50),
        BAZ VARCHAR2(50)
    );

    FUNCTION FOO_FUNCTION RETURN FOO_RECORD;

END;
/

CREATE OR REPLACE PACKAGE BODY FOO_PACKAGE AS

    FUNCTION FOO_FUNCTION RETURN FOO_RECORD AS
        R FOO_RECORD;
    BEGIN
        R.BAR := 'Hello bar!';
        R.BAZ := 'Hello baz!';
        RETURN R;
    END;

END;
/

C#:

The first thing I tried was the most direct approach, but I'm at a loss how to bind the return parameter...

using (var conn = new OracleConnection(connection_string)) {
    conn.Open();
    using (var tran = conn.BeginTransaction()) {
        using (var cmd = conn.CreateCommand()) {
            cmd.CommandText = "FOO_PACKAGE.FOO_FUNCTION";
            cmd.CommandType = CommandType.StoredProcedure;
            cmd.Parameters.Add(
                null,
                /* What to use here? */,
                ParameterDirection.ReturnValue
            );
            cmd.ExecuteNonQuery();
        }
    }
}

I have also tried some "roundabout" approaches, but they all throw exceptions:

cmd.CommandText = "SELECT * FROM FOO_PACKAGE.FOO_FUNCTION";
cmd.ExecuteReader(); // ORA-00942: table or view does not exist

cmd.CommandText = "SELECT FOO_PACKAGE.FOO_FUNCTION FROM DUAL";
cmd.ExecuteReader(); // ORA-00902: invalid datatype

cmd.CommandText = "SELECT BAR, BAZ FROM (SELECT FOO_PACKAGE.FOO_FUNCTION FROM DUAL)";
cmd.ExecuteReader(); // ORA-00904: "BAZ": invalid identifier

回答1:


You need anonymous PL/SQL block to convert function result to another representation:

declare
  vFooRes FOO_PACKAGE.FOO_RECORD;
  vRes sys_refcursor;
begin
  vFooRes := FOO_PACKAGE.FOO_FUNCTION;
  open vRes for select vFooRes.BAR, vFooRes.BAZ from dual;
  --:result := vRes;
end;

SQLfiddle

And execute it instead of calling stored procedure:

cmd.CommandText = "declare\n" +
                  "  vFooRes FOO_PACKAGE.FOO_RECORD;\n" + 
                  "begin\n" + 
                  "  vFooRes := FOO_PACKAGE.FOO_FUNCTION;\n" + 
                  "  open :result for select vFooRes.BAR, vFooRes.BAZ from dual;\n" + 
                  "end;";

OracleParameter p = cmd.Parameters.Add(
                      "result",
                       OracleDbType.RefCursor,
                       DBNull.Value,
                       ParameterDirection.Output
                    );

cmd.ExecuteNonQuery();

After executing cmd you get cursor in result parameter which can be used to fill dataset:

adapter = New OracleDataAdapter(cmd)
data = New DataSet("FooDataSet")
adapter.Fill(data, "result", (OracleRefCursor)(cmd.Parameters["result"].Value));


来源:https://stackoverflow.com/questions/16941214/using-odp-net-to-get-a-record-from-pl-sql-function-without-touching-pl-sql-code

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