После создания прототипа различных подходов мы остановились на следующем.
К IDatabaseService мы добавили новые методы ExecuteYYY, которые принимают объект, реализующий IDatabaseSubroutineSignature и (необязательно, через перегрузку) IEnumerable , которые являются значениями параметров.
Методы ExecuteYYY в IDatabaseService выглядят так:
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));
}
}
}