さまざまなアプローチのプロトタイプを作成した後、次の方法に落ち着きました。
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));
}
}
}