2

従来の Web アプリケーションでは、ストアド プロシージャを多用しています。すべてのデータベース呼び出し (つまり、クエリとプロシージャ) が行われる中央インターフェイスがあります。ただし、現在の実装では、内部でOracleCommandBuilder.DeriveParameters メソッドを使用して、適切なストアド プロシージャ シグネチャにバインドしています。ドキュメントから:

DeriveParameters はデータベース ラウンドトリップを発生させるため、設計時にのみ使用する必要があります。運用環境で不要なデータベース ラウンドトリップを回避するには、DeriveParameters メソッド自体を、設計時に DeriveParameters メソッドによって返された明示的なパラメーター設定に置き換える必要があります。

OracleCommand クラスを使用して、正しいストアド プロシージャ シグネチャに明示的にバインドできます。ただし、OracleCommand オブジェクトを使用してコードを (データ アクセス レイヤーだけであっても) ポイ捨てすることは、データベースにとらわれません。データベース インターフェース (以下、IDatabaseServiceと呼びます) では、データベースに依存しない動的クエリを既にサポートしています。これは次のようになります。

int ExecuteNonQuery(string query, object[] parameterValues);
IDataReader ExecuteReader(string query, object[] parameterValues);
// etc.

データベースに依存しないストアド プロシージャ コールもサポートしたいと考えています。これにはどのようなパターンが適していますか?

詳しくは:

特定のサブルーチンにバインドするために、OracleCommands はBindByNameを許可します。文字列は型よりもエラーが発生しやすいため、このアプローチは使用しないことをお勧めします。サブルーチン呼び出しをバインドするもう 1 つの方法は、パラメーターの型を提供することです。パラメータ値に依存して実行時の型を反映することもできますが、それよりも強力な安全性が必要です。データベースと通信する前に、提供されたパラメーター値が提供されたサブルーチンパラメーターの型と一致することを確認できるように、データベースインターフェイスに型を明示的に提供する必要があります。

4

1 に答える 1

1

さまざまなアプローチのプロトタイプを作成した後、次の方法に落ち着きました。

IDatabaseServiceに、 IDatabaseSubroutineSignatureを実装するオブジェクトと (オプションで、オーバーロードを介して)パラメータ値であるIEnumerableを受け取る新しい ExecuteYYY メソッドを追加しました。

IDatabaseService の ExecuteYYY メソッドはのようになります。

DataSet ExecuteDataSet(IDatabaseSubroutineSignature signature);
DataSet ExecuteDataSet(IDatabaseSubroutineSignature signature, IEnumerable<object> parameterValues);
void ExecuteNonQuery(IDatabaseSubroutineSignature signature);
void ExecuteNonQuery(IDatabaseSubroutineSignature signature, IEnumerable<object> parameterValues);
IDataReader ExecuteReader(IDatabaseSubroutineSignature signature);
IDataReader ExecuteReader(IDatabaseSubroutineSignature signature, IEnumerable<object> parameterValues);
object ExecuteScalar(IDatabaseSubroutineSignature signature);
object ExecuteScalar(IDatabaseSubroutineSignature signature, IEnumerable<object> parameterValues);
ReadOnlyCollection<object> ExecuteScalarMultiple(IDatabaseSubroutineSignature signature);
ReadOnlyCollection<object> ExecuteScalarMultiple(IDatabaseSubroutineSignature signature, IEnumerable<object> parameterValues);

標準の .NET BCL ExecuteYYY メソッドと上記のメソッドにはいくつかの違いがあります。

  • ExecuteNonQuery メソッドは void を返します。これは、ストアド プロシージャが実行されると、(コマンド オブジェクトの) ExecuteNonQuery が常に -1 を返すためです。
  • 新しい ExecuteScalarMultiple メソッドを導入しました。これにより、複数の出力パラメーターが考慮されます。

IDatabaseSubroutineSignatureは次のようになります。

public interface IDatabaseSubroutineSignature
{
    string Name { get; }
    IEnumerable<IDatabaseSubroutineParameter> Parameters { get; }
}

public interface IDatabaseSubroutineParameter
{
    ParameterType Type { get; }
    ParameterDirection Direction { get; }
}

// Using custom DbType attribute.
public enum ParameterType
{
    [DbType(DbType.Decimal)]
    Decimal,
    [DbType(DbType.String)]
    String,
    [DbType(DbType.StringFixedLength)]
    Character,
    RefCursor,
    [DbType(DbType.Double)]
    Double,
    [DbType(DbType.Int32)]
    Int32,
    [DbType(DbType.Int64)]
    Int64,
    [DbType(DbType.DateTime)]
    DateTime
}

私たちが抱えていた最後の問題は、コードで署名を作成 (および表現) する便利な方法にありました。パラメータを作成するためのメソッドを公開する IDatabaseSubroutineSignature のサブインターフェイスを作成することで、モナデスクなアプローチに落ち着きました。

public interface IDatabaseSubroutineSignatureCreator : IDatabaseSubroutineSignature
{
    IDatabaseSubroutineSignatureCreator Input(ParameterType dbType);
    IDatabaseSubroutineSignatureCreator Output(ParameterType dbType);
    IDatabaseSubroutineSignatureCreator InputOutput(ParameterType dbType);
    IDatabaseSubroutineSignatureCreator ReturnValue(ParameterType dbType);
}

最後に、使用例を次に示します。

private static readonly IDatabaseSubroutineSignature MyProcedureSignature =
    DatabaseSubroutineSignatureFactory.Create("pkg.myprocedure")
        .Input(ParameterType.Decimal)
        .Input(ParameterType.String)
        .Output(ParameterType.RefCursor);

public IEnumerable<DataObject> CallMyProcedure(decimal userId, string searchQuery)
{
    using (IDatabaseService dbService = ...)
    using (IDataReader dataReader = dbService.ExecuteReader(MyProcedureSignature,
        new object[] { userId, searchQuery }))
    {
        while (dataReader.Read())
        {
            yield return new DataObject(
                dataReader.GetDecimal(0),
                dataReader.GetString(1));
        }
    }
}
于 2011-10-05T15:37:58.910 に答える