Возrotation DataReader из DataLayer в инструкции Using

У нас есть много кода слоя данных, который следует за этой общей схемой:

public DataTable GetSomeData(string filter) { string sql = "SELECT * FROM [SomeTable] WHERE SomeColumn= @Filter"; DataTable result = new DataTable(); using (SqlConnection cn = new SqlConnection(GetConnectionString())) using (SqlCommand cmd = new SqlCommand(sql, cn)) { cmd.Parameters.Add("@Filter", SqlDbType.NVarChar, 255).Value = filter; result.Load(cmd.ExecuteReader()); } return result; } 

Я думаю, мы можем сделать немного лучше. Моя главная жалоба прямо сейчас заключается в том, что она заставляет все записи загружаться в память даже для больших наборов. Я хотел бы иметь возможность использовать способность DataReader поддерживать только одну запись в ram одновременно, но если я верну DataReader напрямую, соединение будет отключено при выходе из блока использования.

Как я могу улучшить это, чтобы позволить возвращать одну строку за раз?

Еще раз, акт составления моих мыслей по этому вопросу раскрывает ответ. В частности, последнее предложение, в котором я написал «по одной строке за раз». Я понял, что мне все равно, что это datareader, если я могу перечислить его подряд за строкой. Это привело меня к следующему:

 public IEnumerable GetSomeData(string filter) { string sql = "SELECT * FROM [SomeTable] WHERE SomeColumn= @Filter"; using (SqlConnection cn = new SqlConnection(GetConnectionString())) using (SqlCommand cmd = new SqlCommand(sql, cn)) { cmd.Parameters.Add("@Filter", SqlDbType.NVarChar, 255).Value = filter; cn.Open(); using (IDataReader rdr = cmd.ExecuteReader()) { while (rdr.Read()) { yield return (IDataRecord)rdr; } } } } 

Это будет работать еще лучше, как только мы перейдем к 3.5 и можем начать использовать другие операторы linq для результатов, и мне это нравится, потому что он заставляет нас задуматься о «конвейере» между каждым уровнем для запросов, которые возвращают много Результаты.

Нижняя сторона заключается в том, что для читателей, имеющих более одного набора результатов, будет неудобно, но это чрезвычайно редко.

Обновить
Поскольку я впервые начал играть с этим шаблоном в 2009 году, я узнал, что лучше всего, если я также сделаю его Func возвращаемым типом IEnumerable и добавлю параметр Func чтобы преобразовать состояние DataReader в бизнес-объекты в петля. В противном случае могут возникать проблемы с ленивой итерацией, так что каждый раз вы видите последний объект в запросе.

То, что вы хотите, – это поддерживаемый шаблон, вам придется использовать

 cmd.ExecuteReader(CommandBehavior.CloseConnection); 

и удалите оба using() using () из метода GetSomeData (). Обеспечение безопасности исключений должно предоставляться вызывающим абонентом, гарантируя закрытие устройства.

В такие моменты я нахожу, что лямбды могут быть полезны. Подумайте об этом, вместо слоя данных, дающего нам данные, дадим слой данных наш метод обработки данных:

 public void GetSomeData(string filter, Action processor) { ... using (IDataReader reader = cmd.ExecuteReader()) { processor(reader); } } 

Тогда бизнес-уровень будет называть его:

 GetSomeData("my filter", (IDataReader reader) => { while (reader.Read()) { ... } }); 

Ключевым является ключевое слово yield .

Как и в оригинальном ответе Джоэля, немного больше конкретизировалось:

 public IEnumerable Get(string query, Action parameterizer, Func selector) { using (var conn = new T()) //your connection object { using (var cmd = conn.CreateCommand()) { if (parameterizer != null) parameterizer(cmd); cmd.CommandText = query; cmd.Connection.ConnectionString = _connectionString; cmd.Connection.Open(); using (var r = cmd.ExecuteReader()) while (r.Read()) yield return selector(r); } } } 

И у меня есть этот метод расширения:

 public static void Parameterize(this IDbCommand command, string name, object value) { var parameter = command.CreateParameter(); parameter.ParameterName = name; parameter.Value = value; command.Parameters.Add(parameter); } 

Поэтому я звоню:

 foreach(var user in Get(query, cmd => cmd.Parameterize("saved", 1), userSelector)) { } 

Это полностью общее, подходит для любой модели, соответствующей интерфейсам ado.net. Объекты соединения и считывания расположены после enums коллекции. Во всяком случае, заполнение DataTable с использованием IDataAdapter Fill IDataAdapter может быть быстрее, чем DataTable.Load

Я никогда не был большим поклонником того, чтобы слой данных возвращал общий объект данных, поскольку это в значительной степени растворяет всю суть того, что код разделен на свой собственный уровень (как вы можете отключить слои данных, если интерфейс не определен? ).

Я считаю, что лучше всего для всех таких функций вернуть список пользовательских объектов, которые вы создаете сами, а в ваших данных позже вы вызываете свою процедуру / запрос в datareader и повторяете это, создавая список.

Это упростит работу в целом (несмотря на начальное время создания пользовательских classов), упрощает обработку вашего соединения (так как вы не будете возвращать какие-либо связанные с ним объекты) и должны быть быстрее. Единственный недостаток – все будет загружено в память, как вы упомянули, но я бы не подумал, что это будет причиной беспокойства (если бы это было так, я бы подумал, что запрос нужно будет скорректировать).

  • Как отключить каскадное удаление для таблиц ссылок в EF-кодах?
  • Заполнение combobox из базы данных с помощью hibernate в Java
  • Как создать HashCode в .net (c #) для строки, безопасной для хранения в базе данных?
  • LINQ to SQL Left Outer Join
  • Синхронные запросы к базе данных с помощью Node.js
  • @GeneratedValue полиморфный абстрактный суперclass над MySQL
  • Не удается подключиться к серверу MySQL на «127.0.0.1» (10061) (2003)
  • Групповая агрегация Mongodb $ group, ограничить длину массива
  • JPA с использованием нескольких схем баз данных
  • Используйте функцию динамического ключевого слова / .NET 4.6 в Unity
  • Поиск в Firebase без кода на стороне сервера
  • Interesting Posts

    Изменить шрифт для не-устаревшей командной строки в Windows 10 с не-ASCII-кодировкой – Свойства по умолчанию?

    Неразрешенные символы при связывании программы с использованием libcurl

    Почему массивы ссылок являются незаконными?

    Entity Framework: я устанавливаю внешний ключ, SaveChanges затем получает доступ к свойству навигации, но не загружает связанный объект. Почему нет?

    ggplot, объединяющий два графика из разных data.frames

    Пользователь Windows только для одного

    GET изображения с URL-адреса, а затем переименуйте изображение

    IE9 JSON Data “вы хотите открыть или сохранить этот файл”

    Откройте существующий файл, добавьте одну строку

    Может ли 32-разрядная ОС работать в 64-битном процессоре?

    Чтобы издеваться над объектом, нужно ли реализовать интерфейс или помеченный виртуальный?

    Mongoose.js: удалить коллекцию или базу данных

    Не удается добавить сетевой принтер – Windows 7

    Существует ли межсегментный авторезистор высоты iframe, который работает?

    Windows 8.1 100% использование диска

    Давайте будем гением компьютера.