Oracle 使用ODP.NET从PL/SQL函数获取记录,而不涉及PL/SQL代码

Oracle 使用ODP.NET从PL/SQL函数获取记录,而不涉及PL/SQL代码,oracle,record,odp.net,Oracle,Record,Odp.net,这个标题是不言自明的:从一个C#应用程序,使用ODP.NET,我试图调用一个PL/SQL函数,它返回的不是简单的值,而是一条记录 不幸的是,我无权添加或更改PL/SQL代码,因此尝试将函数包装到另一个返回不同类型的函数中对我来说不是一个选项 下面是一个简化的例子 PL/SQL: CREATE OR REPLACE PACKAGE FOO_PACKAGE AS TYPE FOO_RECORD IS RECORD ( BAR VARCHAR2(50), BA

这个标题是不言自明的:从一个C#应用程序,使用ODP.NET,我试图调用一个PL/SQL函数,它返回的不是简单的值,而是一条记录

不幸的是,我无权添加或更改PL/SQL代码,因此尝试将函数包装到另一个返回不同类型的函数中对我来说不是一个选项

下面是一个简化的例子

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#:

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;
/
我尝试的第一件事是最直接的方法,但我不知道如何绑定返回参数

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();
        }
    }
}
我也尝试过一些“迂回”的方法,但它们都会抛出异常:

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
您需要将函数结果转换为其他表示形式:

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;

并执行它,而不是调用存储过程:

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();
执行
cmd
后,光标进入
result
参数,该参数可用于填充数据集:

var adapter = new OracleDataAdapter(cmd);
var data = new DataSet("FooDataSet");
adapter.Fill(data, "result", (OracleRefCursor)(p.Value));