3.12 暗黙的なREF CURSORのバインド

ODP.NETでは、.NETコードにREF CURSORパラメータを明示的にバインドせずに、このパラメータを使用するストアド・プロシージャをアプリケーションで実行できます。ODP.NET管理対象外ドライバと管理対象ドライバは、.NET構成ファイルで実行される構成を通じて、REF CURSORの暗黙的なバインディングをサポートします。

読取り専用の結果セット(OracleDataReaderを使用するREF CURSORなど)では、REF CURSORのスキーマ情報が自動的に取得されます。

更新可能なREF CURSORやEntity Frameworkを使用する場合など、開発者は、アプリケーションで暗黙的な REF CURSORをバインドできるように、REF CURSORのスキーマ情報を定義する必要のある場合があります。Entity Frameworkアプリケーションでは、暗黙的なREF CURSORのバインドを使用して、REF CURSORデータから複合型がインスタンス化されます。アプリケーションのapp.configweb.configまたはmachine.config .NET構成ファイルで、REF CURSORのバインド情報およびメタデータ情報を指定する必要があります。

.NET構成ファイルで指定した属性は、REF CURSORを表すOracleDataReaderオブジェクトからスキーマ情報をリクエストする際にも使用されます。これは、SELECTを使用して単一表から作成されるREF CURSORについて、OracleDataAdapterおよびOracleCommandBuilderを使用してその表を更新できることを意味します。

Entity Frameworkを使用する際は、関数インポートによって、明示的にバインドされたREF CURSORが戻されます。REF CURSORは、複合型またはエンティティ型の集合として戻されます。複合型の集合を戻すには、.NET構成ファイルでREF CURSORのバインド情報およびメタデータ情報を定義する必要があります。エンティティ型の集合を戻すには、.NET構成ファイルでバインド情報のみを定義する必要があります。

この項には次のトピックが含まれます:

3.12.1 .NET構成ファイルでのREF CURSORのバインド情報およびメタデータ情報の指定

REF CURSORの情報は、.NET構成ファイルのoracle.dataacccess.client構成セクションで指定します。各情報に対して<add>要素を使用します。add要素では、name-value属性を使用してREF CURSORの情報を指定します。バインド情報を指定するには、次のフォーマットを使用します。

<add name="SchemaName.PackageName.StoredProcedureName.RefCursor.RefCursorParameterPositionOrName" 
value="implicitRefCursor bindinfo='mode=InputOutput|Output|ReturnValue'" />

メタデータ情報を指定するには、次のフォーマットを使用します。

<add name="SchemaName.PackageName.StoredProcedureName.RefCursorMetaData.RefCursorParameterPositionorName.Column.ColumnOrdinal" 
value="implicitRefCursor metadata=AttributesList" />

add要素は、REF CURSORの各列に対して定義する必要があります。たとえば、5つの列を返すREF CURSORでは、構成ファイルで5つのadd要素を定義する必要があります。

add要素には、name属性およびvalue属性が含まれます。value属性は、implicitRefCursorで始まり、次にバインド情報またはメタデータ情報を指定するためのbindinfo属性またはmetadata属性が続く必要があります。

bindinfo情報は、ODP.NETで、REF CURSORパラメータのバインドに使用されます。metadata情報は、ODP.NETで、スキーマ情報を適切なREF CURSORと関連付けるために使用されます。メタデータは、パラメータとその値を含む属性リストで構成されます。

SchemaNamePackageNameおよびStoredProcedureNameでは、大/小文字が区別されます。暗黙的なREF CURSORのバインドを使用するストアド・プロシージャを実行するには、name属性のSchemaName.PackageName.StoredProcedureNameが、そのストアド・プロシージャのデータ・ディクショナリに指定されている名前と完全に一致している必要があります。

注意:

Entity Framework外で暗黙的なREF CURSORのバインド機能を使用する場合は、.NET構成ファイルおよびOracleCommand CommandText で、ストアド・プロシージャ名の前にスキーマ名が連結されている必要がありません。

データベースのスキーマ名、パッケージ名またはストアド・プロシージャ名に小文字が含まれる場合は、小文字が保持されるように、構成ファイルで名前を二重引用符(")で囲む必要があります。name属性内で二重引用符を使用する必要がある場合は、&quot;を使用します。たとえば、データベースのスキーマ名がHrSchema、パッケージ名がHrPackage、ストアド・プロシージャ名がHrStoredProcedureである場合、構成ファイルには次を使用する必要があります。

<add name="&quot;HrSchema&quot;.&quot;HrPackage&quot;.&quot;HrStoredProcedure&quot;.RefCursorMetaData . . . />

デフォルトで、これらの名前はOracle Databaseに大文字で格納されます。ODP.NETではデフォルトの動作が想定されるため、二重引用符を使用して明示的に小文字を保持しないかぎり、すべての名前が大文字に変換されます。

注意:

SchemaNamePackageNameStoredProcedureNameまたはParameterNameは、名前にピリオド(「.」)を含むことができません。たとえば、P.0はパラメータ名として許可されません。

名前属性のRefCursorParameterPositionOrNameは、名前指定によるバインドと位置指定によるバインドのいずれを使用するかに基づいて、パラメータ位置(位置指定によるバインドの場合)またはパラメータ名(名前指定によるバインドの場合)に適切に設定する必要があります。ファンクションでは、位置は0ベースであり、位置0は戻り値を表します。プロシージャでは、戻り値が存在しないため、位置は1ベースです。たとえば、ストアド・プロシージャで5つのパラメータが許可され、3番目と5番目のパラメータ位置にある2つのREF CURSORのみが返される場合、.NET config REF CURSORのバインド情報には、位置3に1つと位置5に1つのエントリが含まれます。

名前によるバインドを使用する場合は、属性名によってREF CURSORパラメータを識別します。nameには、そのストアド・プロシージャのデータ・ディクショナリで指定されている名前と同じ名前を、大/小文字を区別して使用する必要があります。

bindinfoで、modeはパラメータのディレクションを指定します。モードは、InputOutputOutputReturnValueのいずれかである必要があります。

注意:

入力パラメータとしてのREF CURSORについては、暗黙的なREF CURSORのバインドがサポートされていません。

modeInputに設定されているREF CURSORのエントリが.NET構成ファイルに含まれる場合は、実行時に例外がスローされます。

metadataでは、AttributesListにパラメータのリストが含まれます。表3-18に、AttributesListに含むことのできるパラメータを示します。

例3-5に、bindinfoを使用するadd要素のサンプルを示します。ここで、スキーマ名はSCOTTで、ストアド・プロシージャ名はTESTPROCです。パラメータ名はparameter1です。モードはoutputです。

例3-6に、metadataを使用するadd要素のサンプルを示します。

表3-18 属性リストで許可されるパラメータ

名前 Type Entity Frameworkで必須/オプション 説明

ColumnName

System.String

必須

列の名前です。

ProviderType

Oracle.DataAccess.Client.OracleDbType

必須

列のデータベース列タイプ(OracleDbType)

NativeDataType

System.String

必須

Oracleタイプ。たとえば、NCLOBです。

BaseColumnName

System.String

オプション

列に別名が使用されている場合の、データベース内の列名。

BaseSchemaName

System.String

オプション

列を含むデータベース内のスキーマ名。

BaseTableName

System.String

オプション

列を含むデータベース内の表名またはビュー名。

ColumnSize

System.Int64

オプション

列内の値として可能な最大長

NumericPrecision

System.Int16

オプション

列が数値のデータ型の場合の、列の最大精度。

NumericScale

System.Int16

オプション

列が数値のデータ型の場合の、列の最大スケール。

IsUnique

System.Boolean

オプション

列値が一意かどうかを示します。

IsKey

System.Boolean

オプション

列がキー列かどうかを示します。REF CURSORの情報を使用して更新する表では、REF CURSORメタデータの1つ以上の列で、この値がtrueに設定されている必要があります

IsRowID

System.Boolean

オプション

列がROWIDの場合はtrue、それ以外の場合はfalse

DataType

System.RuntimeType

オプション

共通言語のランタイム型にマップします。

AllowDBNull

System.Boolean

オプション

null値が許可されている場合はtrue、それ以外の場合はfalse

IsAliased

System.Boolean

オプション

列が別名の場合はtrue、それ以外の場合はfalse

IsByteSemantic

System.Boolean

オプション

IsByteSemanticの場合は次のとおりです。

  • ColumnSize値にバイト・セマンティクスが使用されている場合はtrue

  • ColumnSizeにキャラクタ・セマンティクスが使用されている場合はfalse

IsExpression

System.Boolean

オプション

列が式の場合はtrue、それ以外の場合はfalse

IsHidden

System.Boolean

オプション

列が非表示の場合はtrue、それ以外の場合はfalse

IsReadOnly

System.Boolean

オプション

列が読取り専用の場合はtrue、それ以外の場合はfalse

IsLong

System.Boolean

オプション

true(列がLONG、LONG RAW、BLOB、CLOB または

BFILE型の場合)、false(それ以外の場合)。

UdtTypeName

System.String

オプション

UDTのタイプ名。

ProviderDBType

System.Data.DbType

オプション

System.Data.DbType

ObjectName

System.String

オプション

オブジェクトの名前を表します。

表3-18にリストされている一部の属性については、結果セットのメタデータを使用して、値が自動的に設定されます。開発者は、明示的に値を設定して、デフォルトの値を上書きできます。

特定の操作でオプションとしてリストされている一部の属性について、明示的に定義する必要のある場合があります。たとえば、更新可能なREF CURSORでは、キー情報を定義する必要があります。

例3-5 bindinfoを含むadd要素の使用

<add name="SCOTT.TESTPROC.RefCursor.parameter1" value="implicitRefCursor
 bindinfo='mode=Output'" />

例3-6 メタデータを含むadd要素の使用

<add name="scott.TestProc.RefCursorMetaData.parameter1.Column.0" 
value="implicitRefCursor metadata='ColumnName=EMPNO;BaseColumnName=EMPNO;
BaseSchemaName=SCOTT;BaseTableName=EMP;NativeDataType=number;
ProviderType=Int32;DataType=System.Int32;ColumnSize=4;AllowDBNull=false;
IsKey=true'" />

3.12.2 構成ファイルおよびアプリケーションのサンプル

この項では、サンプル・アプリケーションを構築して、暗黙的なREF CURSORのバインドを説明します。この章の内容は、次のとおりです。

ストアド・プロシージャおよびストアド・ファンクションのサンプル

CREATE OR REPLACE FUNCTION GETEMP (
  EMPID IN NUMBER) return sys_refcursor is
  emp sys_refcursor;
BEGIN
  OPEN emp FOR SELECT empno, ename FROM emp where empno = EMPID;
  return emp;
END;
/
 
CREATE OR REPLACE PROCEDURE "GetEmpAndDept" (
  EMPS OUT sys_refcursor,
  DEPTS OUT sys_refcursor) AS
BEGIN
  OPEN EMPS for SELECT empno, ename from emp;
  OPEN DEPTS for SELECT deptno, dname from dept;
END;
/

アプリケーション構成ファイルのサンプル

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <oracle.dataaccess.client>
    <settings>
 
      <!-- The following is for SCOTT.GETEMP -->
      <add name="SCOTT.GETEMP.RefCursor.0" 
           value="implicitRefCursor bindinfo='mode=ReturnValue'" />
 
      <!-- The following is for SCOTT.GETEMP's REF CURSOR metadata -->
      <add name="SCOTT.GETEMP.RefCursorMetaData.0.Column.0"
           value="implicitRefCursor metadata='ColumnName=EMPNO;
           BaseColumnName=EMPNO;BaseSchemaName=SCOTT;BaseTableName=EMP;
           NativeDataType=number;ProviderType=Int32;ProviderDBType=Int32;
           DataType=System.Int32;ColumnSize=4;NumericPrecision=10;
           NumericScale=3;AllowDBNull=false;IsKey=true'" />
 
      <add name="SCOTT.GETEMP.RefCursorMetaData.0.Column.1"
           value="implicitRefCursor metadata='ColumnName=ENAME;
           BaseColumnName=ENAME;BaseSchemaName=SCOTT;BaseTableName=EMP;
           NativeDataType=varchar2;ProviderType=Varchar2;
           ProviderDBType=String;DataType=System.String;
           ColumnSize=10;AllowDBNull=true'" />
 
      <!-- The following is for "SCOTT"."GetEmpAndDept" -->
      <add name="SCOTT.&quot;GetEmpAndDept&quot;.RefCursor.EMPS" 
           value="implicitRefCursor bindinfo='mode=Output'" />
 
      <!-- The following is for SCOTT.GETEMP's EMPS REF CURSOR metadata -->
      <add name="SCOTT.&quot;GetEmpAndDept&quot;
           .RefCursorMetaData.EMPS.Column.0"
           value="implicitRefCursor metadata='ColumnName=EMPNO;
           BaseColumnName=EMPNO;BaseSchemaName=SCOTT;BaseTableName=EMP;
           NativeDataType=number;ProviderType=Int32;ProviderDBType=Int32;
           DataType=System.Int32;ColumnSize=4;NumericPrecision=10;
           NumericScale=3;AllowDBNull=false;IsKey=true'" />
 
      <add name="SCOTT.&quot;GetEmpAndDept&quot;
           .RefCursorMetaData.EMPS.Column.1"
           value="implicitRefCursor metadata='ColumnName=ENAME;
           BaseColumnName=ENAME;BaseSchemaName=SCOTT;BaseTableName=EMP;
           NativeDataType=varchar2;ProviderType=Varchar2;
           ProviderDBType=String;DataType=System.String;
           ColumnSize=10;AllowDBNull=true'" />
 
      <!-- The following is for SCOTT.GETEMP's DEPTS REF CURSOR metadata -->
      <add name="SCOTT.&quot;GetEmpAndDept&quot;.RefCursor.DEPTS" 
           value="implicitRefCursor bindinfo='mode=Output'" />
 
      <add name="SCOTT.&quot;GetEmpAndDept&quot;
           .RefCursorMetaData.DEPTS.Column.0"
           value="implicitRefCursor metadata='ColumnName=DEPTNO;
           BaseColumnName=DEPTNO;BaseSchemaName=SCOTT;BaseTableName=DEPT;
           NativeDataType=number;ProviderType=Int32;ProviderDBType=Int32;
           DataType=System.Int32;ColumnSize=4;NumericPrecision=10;
           NumericScale=3;AllowDBNull=false;IsKey=true'" />
 
      <add name="SCOTT.&quot;GetEmpAndDept&quot;
           .RefCursorMetaData.DEPTS.Column.1"
           value="implicitRefCursor metadata='ColumnName=DNAME;
           BaseColumnName=DNAME;BaseSchemaName=SCOTT;BaseTableName=DEPT;
           NativeDataType=varchar2;ProviderType=Varchar2;
           ProviderDBType=String;DataType=System.String;
           ColumnSize=10;AllowDBNull=true'" />
      </settings>
  </oracle.dataaccess.client>
</configuration>

構成ファイルを使用するアプリケーションのサンプル

using System;
using System.Data;
using Oracle.DataAccess.Client;
 
class Program
{
  static void Main(string[] args)
  {
    try
    {
      // Open a connection
      string constr =
        "User Id=scott;Password=tiger;Data Source=inst1";
      OracleConnection con = new OracleConnection(constr);
      con.Open();
 
      // Use implicit REF CURSOR binding 
      //   to execute SCOTT.GETEMP function
      // Use bind by position as configured 
      //   in app.config for SCOT.GETEMP
      OracleCommand cmd = con.CreateCommand();
      cmd.CommandText = "SCOTT.GETEMP";
      cmd.CommandType = CommandType.StoredProcedure;
      cmd.BindByName = false;
      OracleParameter empid = cmd.Parameters.Add("empid", 
        OracleDbType.Int32, ParameterDirection.Input);
      empid.Value = 7654;
 
      // Populate the DataSet
      OracleDataAdapter adapter = new OracleDataAdapter(cmd);
      DataSet ds = new DataSet();
      adapter.Fill(ds);
      Console.WriteLine("Retrieved {0} row from EMP", 
        ds.Tables[0].Rows.Count);
 
      // Use implicit REF CURSOR binding 
      //   to execute "SCOTT"."GetEmpAndDept" procedure
      // Use bind by name as configured 
      //   in app.config for "SCOTT"."GetEmpAndDept"
      cmd = con.CreateCommand();
      cmd.CommandText = "\"SCOTT\".\"GetEmpAndDept\"";
      cmd.CommandType = CommandType.StoredProcedure;
      cmd.BindByName = true;
      adapter = new OracleDataAdapter(cmd);
      adapter.Fill(ds);
      Console.WriteLine("Retrieved {0} rows from DEPT", 
        ds.Tables[1].Rows.Count);
    }
    catch (Exception ex)
    {
      // Output the message
      Console.WriteLine(ex.Message);
      if (ex.InnerException != null)
      {
        // If any details are available regarding
        // errors in the app.config, print them out
        Console.WriteLine(ex.InnerException.Message);
        if (ex.InnerException.InnerException != null)
        {
          Console.WriteLine(
            ex.InnerException.InnerException.Message);
        }
      }
    }
  }
}

3.12.3 使用上の考慮事項

この項では、暗黙的なREF CURSORの使用に関する次の考慮事項について説明します。

3.12.3.1 CommandTextプロパティに関する考慮事項

ODP.NETアプリケーションでは、ストアド・プロシージャ名とOracleCommand CommandTextが完全に一致している必要があります。データベースのストアド・プロシージャ名がSCOTT.TESTPROCであると仮定します。この場合にCommandTextTESTPROCを使用すると、ODP.NETによって、TESTPROCに一致するエントリのみが検索されます。TESTPROCに現行のスキーマ名が自動的に追加されません。このため、ここでは適切なCommandTextとして、SCOTT.TESTPROCを使用する必要があります。

また、CommandTextでは大/小文字が区別されるため、データベースのストアド・プロシージャ名と同じ大/小文字を使用する必要があります。このため、データベースのストアド・プロシージャ名がSCOTT.Testprocである場合は、CommandTextSCOTT.Testprocを使用する必要があります。

3.12.3.2 バインドに関する考慮事項

REF CURSORパラメータに関する情報が構成ファイルに追加された場合、アプリケーションで、OracleCommandREF CURSORパラメータを明示的にバインドしないようにします。ODP.NETでは、構成ファイルで指定された情報に基づいて、REF CURSORパラメータが適切な位置に自動的にバインドされます。アプリケーションのストアド・プロシージャにREF CURSOR以外のバラメータも存在する場合、これらのパラメータについては、引き続き明示的にOracleCommandにバインドする必要があります。

ストアド・プロシージャの構成ファイルで指定されている情報で、REF CURSORパラメータが名前で識別されている場合は、REF CURSOR以外のすべてのパラメータも名前でバインドする必要があります。また、この場合は、OracleCommandオブジェクトのBindByNameプロパティをtrueに設定する必要があります。Entity Frameworkでは、ストアド・プロシージャの実行に常にBindByNameが使用されます。.NET構成ファイルのパラメータ名には、データベースでストアド・プロシージャを作成する際に使用したものと同じ大/小文字を使用する必要があります。

OracleCommand BindByNameプロパティがfalseに設定されている場合(デフォルト)、ODP.NETでは、パラメータが位置に基づいてバインドされ、かつすべてのパラメータが適切な順序で指定されていると想定されます。この場合、構成ファイルで指定されているパラメータは、構成ファイルに表示される順序と同じ順序でバインドされます。

3.12.3.3 オーバーロードされたストアド・プロシージャ

ODP.NETでは、構成ファイル内に、同じ名前のストアド・プロシージャが複数存在することはできません。オーバーロードされたストアド・プロシージャを使用すると、ODP.NETアプリケーションの構成ファイルには、オーバーロードされた1つのストアド・プロシージャの情報のみが格納されます。

3.12.3.4 タイプの初期化時の例外

タイプの初期化時の例外は、.NET構成ファイルに無効なエントリがある場合に発生することがあります。捕捉した例外と内部例外を評価して、例外を発生させている.NET構成ファイル・エントリまたは属性設定を確認します。

ODP.NETトレースは、ODP.NETによって解析された.NET構成ファイル・エントリのうち、有効なエントリおよび無効なエントリを記録します。.NET構成ファイルに関連するエントリを検索するには、TraceLevel開始、終了およびSQL文の情報レベルに設定します。エラーが発生している場合、暗黙的なREF CURSORのバインドに関連するトレース・エントリには、(REFCURSOR)エントリとともに(ERROR)が表示されます。

3.12.3.5 関数インポートでのストアド・ファンクションの使用

関数インポートでは、ストアド・プロシージャのみがサポートされ、ストアド・ファンクションはサポートされません。ストアド・ファンクションが.NET構成ファイルで適切に構成されている場合であっても、作成したAdd Function Import ダイアログをEntity Data Modelで使用する際に、ストアド・ファンクションによって返されるREF CURSORのメタデータ情報がGet Column Informationボタンによって返されません。