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
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:
var adapter = new OracleDataAdapter(cmd);
var data = new DataSet("FooDataSet");
adapter.Fill(data, "result", (OracleRefCursor)(p.Value));
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With