有没有办法检索PL / SQL存储过程返回的列名和数据类型?

时间:2015-02-10 19:28:34

标签: c# oracle entity-framework plsql odp.net

我正在使用Entity Framework 6和ODP.NET,并将存储过程导入到应用程序的数据模型中。 ODP.NET无法自动导入使用In / Out游标的存储过程,并且需要手动将其信息添加到app.config中。更糟糕的是,我有三十多个导入程序,其中大部分都使用In / Out游标。我已经看到generate config information automatically有可能,但这似乎只适用于直接返回游标的函数。 (也许还有Out游标?)

是否有一种自动方法可以检索存储过程游标返回的列名和数据类型,而无需手动手动运行所有信息?

我当前如何在app.config中导入过程的一个示例,为公共消费进行了清理:

<storedProcedure schema="SCHEMA" name="PACKAGE.PROCEDURENAME">
      <refCursor name="ioCursor">
        <bindInfo mode="InputOutput"/>
        <metadata columnOrdinal="0" columnName="SOMEID" providerType="Int32" allowDBNull="false" nativeDataType="Number"/>            
        <metadata columnOrdinal="1" columnName="SOMEOTHERID" providerType="Int32" allowDBNull="false" nativeDataType="Number"/>
        <metadata columnOrdinal="2" columnName="SOMESTRING" providerType="Varchar2" allowDBNull="false" nativeDataType="Varchar2"/>
      </refCursor>
    </storedProcedure>

如何在程序包中出现相同的程序:

PROCEDURE PROCEDURENAME (
    someInputId IN schema.table.column%TYPE
    ,ioCursor IN OUT t_ref_cur
    )

IS

BEGIN
    vProcedureName := 'PROCEDURENAME';
    OPEN ioCursor FOR SELECT a.some_id SOMEID
                            ,a.some_other_id SOMEOTHERID
                            ,b.some_string SOMESTRING
                        FROM  schema.table_a a,
                              schema.table_b b
                        WHERE (selection criteria removed for brevity's sake)
END

正如您所看到的,为了正确填写app.config中的元数据,我需要运行SOMEID,SOMEOTHERID和SOMESTRING的名称和类型,这三者都可能位于不同的表中。自动提取这些数据的一些方法很可爱。

〜EDIT〜:

解决!

感谢Christian Shay在下面的回答,我最终提出了一些代码,我能够构建一个工具。它与ODP.NET在Server Explorer中的“Run Stored Procedure”完全相同,但它也适用于InOut Ref Cursors!下面有一些伪代码,以显示我最终做的基本想法。

using(ContextItem context = new ContextItem())
{
    OracleCommand cmd = context.Database.Connection.CreateCommand() as OracleCommand;
    cmd.CommandType = CommandType.StoredProcedure;
    cmd.CommandText = "PROCEDURE_NAME_HERE";

    //do the following for all of the input parameters for the stored proc
    OracleDbType paramType = OracleDbType.SomeType;
    OracleParameter param = new OracleParameter("parameterName", paramType);
    param.Value = someValue;

    //or, for cursors
    OracleDbType cursorType = OracleDbType.RefCursor;
    ParameterDirection direction = ParameterDirection.SomeCursorDirection;
    OracleParameter cursorParam = new OracleParameter("cursorName", OracleDbType.RefCursor, direction);

    cmd.Parameters.Add(param);
    cmd.Parameters.Add(cursorParam);
    if (db.Database.Connection.State != ConnectionState.Open) 
    {
        db.Database.Connection.Open();  
    }
    var reader = cmd.ExecuteReader(CommandBehavior.KeyInfo);
    var table = reader.GetSchemaTable();

    for(int i = 0; i < reader.FieldCount; i++)
    {
        /*Now you have access to everything you need for an EF config: 
        Ordinals, column names, provider types, not-null-ness, and native data types*/
    }
} 

3 个答案:

答案 0 :(得分:1)

您可以选择这样的元数据:

SELECT *
  FROM ALL_ARGUMENTS
  WHERE OWNER = '<your schema name>'
  and object_name = '<your procedure name>'

示例输出: enter image description here

答案 1 :(得分:1)

下面是一些示例代码,展示了如何在不将代码导入模型的情况下从EF代码调用procs - 实际上是在拉出OracleCommand对象:

var ctx = new TestContext();
var cmd = ctx.Database.Connection.CreateCommand() as OracleCommand;
cmd.CommandType = CommandType.StoredProcedure;
cmd.CommandText = "SOMESTOREDPROC";
var p_rc1 = new OracleParameter("p_rc1", OracleDbType.RefCursor, ParameterDirection.Output);
var p_rc2 = new OracleParameter("p_rc2", OracleDbType.RefCursor, ParameterDirection.Output);
cmd.Parameters.Add(p_rc1);
cmd.Parameters.Add(p_rc2);

if (ctx.Database.Connection.State != ConnectionState.Open)
    ctx.Database.Connection.Open();

var reader = cmd.ExecuteReader(); 

答案 2 :(得分:0)

您无需手动添加元数据。查看Oracle Developer Tools for Visual Studio联机帮助中的实体框架章节。使用“运行存储过程”对话框执行此操作。它会自动将元数据添加到配置文件中。

这是一个演练: https://apexapps.oracle.com/pls/apex/f?p=44785:24:106203805408474:::24:P24_CONTENT_ID,P24_PROD_SECTION_GRP_ID,P24_PREV_PAGE:10068,,24

请参阅那里的函数导入部分。

注意:“导入功能”对话框仅使用存储过程中的第一个REF CURSOR。它按惯例成为导入的实体函数的返回值。如果你有多个REF CURSOR,你可能需要用另一个SP包装这些SP。

此外,在您的代码中,您没有使用REF CURSOR。您正在使用“t_ref_cur”。那是什么?如果它是用户定义的类型,这将不起作用,您将需要至少包装它以返回实际的REF CURSOR,因为在这种情况下不支持用户定义类型(并且上面提到的运行过程对话框也不喜欢它)