Давайте посмотрим на пример, который мы собираемся улучшить. Возьмем пример из MSDN о SqlCommand:
string commandText = "SELECT OrderID, CustomerID FROM dbo.Orders;";
using (var connection = new SqlConnection(Settings.Default.NorthwindConnectionString)) {
using (var command = new SqlCommand(commandText, connection)) {
connection.Open();
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
Console.WriteLine(String.Format("{0}, {1}", reader[0], reader[1]));
}
}
}
}
Давайте сейчас предположим, что мы мы заинтересованы только в одном select-запросе, который даст возможность нам несколько абстрагироваться от деталей в SQL. Написав несколько небольших хелперов, который используют dynamic, мы сможем написать довольно простой код:
string commandText = "SELECT OrderID, CustomerID FROM dbo.Orders;";
foreach (var row in SimpleQuery.Execute(Settings.Default.NorthwindConnectionString, commandText)) {
Console.WriteLine(String.Format("{0}, {1}", row.OrderID, row.CustomerID));
}
Некоторые примечания:
Нам достаточно сделать единственный вызов и прямо получить объекты с которыми мыможем работать. Сравните это с использованием SqlConnection, SqlCommand и SqlDataReader.
Мы используем стандартный шаблон перечисления, в котором SqlDataReader делает вызов reader.Read() на каждой итерации, что выглядит ужасно.
И еще одно важное: мы получаем прямой доступ к свойствам объекта строки благодаря динамическому объекту! Например мы можем написать row.OrderID вместо reader[0](или reader[“OrderID”])
Как же это работает? Первое, давайте посмотрим на SimpleQuery.Execute метод:
public static IEnumerable<dynamic> Execute(string connString, string commandText) {
using (var connection = new SqlConnection(connString)) {
using (var command = new SqlCommand(commandText, connection)) {
connection.Open();
using (SqlDataReader reader = command.ExecuteReader()) {
while (reader.Read()) {
yield return new DataRecordDynamicWrapper(reader);
}
}
}
}
}
Это собственно тоже самое что и в MSDN коде за исключением что оно оборачивает возвращаемый reader в DataRecordDynamicWrapper, который делает всю волшебную динамическую работу. Также заметьте что метод возвращает IEnumerable
Так что сейчас все что осталось так это взглянуть на DataRecordDynamicWrapper, который невероятно прост:
public class DataRecordDynamicWrapper : DynamicObject {
private IDataRecord _dataRecord;
public DataRecordDynamicWrapper(IDataRecord dataRecord) { _dataRecord = dataRecord; }
public override bool TryGetMember(GetMemberBinder binder, out object result) {
result = _dataRecord[binder.Name];
return result != null;
}
}
Все что он делает, так это получает данные по индексу в _dataRecord для заданного имени свойства.
Единственная последняя вещь, которая ничего не стоит для того чтобы сделать реальной, мы должны добавить поддержку SQL параметров, которые дадут простоту написания SQL кода, не подверженного SQL-инъекциям. Это может быть просто сделано передавая параметры в SimpleQuery.Execute.
Вольный перевод статьи David Ebbo (Исходный код там же)
Комментариев нет:
Отправить комментарий