Средство чтения данных или набор данных при извлечении нескольких наборов записей в ASP.NET

#asp.net #performance #dataset #sqldatareader

Вопрос:

У меня есть ASP.NET страница, на которой есть множество элементов управления, которые необходимо заполнить (например, выпадающие списки).

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

Я мог бы вернуть несколько таблиц в наборе данных или я мог бы вернуть средство чтения данных и использовать». NextResult», чтобы поместить каждый набор результатов в пользовательский бизнес-класс.

Скорее всего, я увижу достаточно большое преимущество в производительности при использовании метода DataReader или мне следует просто использовать подход с набором данных?

Любые примеры того, как вы обычно справляетесь с этим, будут оценены по достоинству.

Ответ №1:

  1. Если у вас есть более 1000 записей, которые нужно извлечь из вашей базы данных.
  2. Если вас не очень интересует пользовательское хранение и пользовательская подкачка «Для GridView«
  3. Если у вашего сервера проблемы с памятью.
  4. Если нет проблем с подключением к вашей базе данных при каждом вызове этой страницы.

Тогда я думаю, что лучше использовать DataReader.

ещё

  1. Если у вас есть менее 1000 записей, которые нужно извлечь из вашей базы данных.
  2. Если вас интересует хранение и подкачка «Для GridView«
  3. Если у вашего сервера нет проблем с памятью.
  4. Если вы хотите подключиться к своей базе данных только один раз и получить преимущества кэширования.

Тогда я думаю, что лучше использовать набор данных.

Я надеюсь, что я прав.

Ответ №2:

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

Комментарии:

1. Под «всегда» ты имеешь в виду «иногда»?

2. Не делай этого! Не передавайте их по кругу! содрогается

3. Из с трудом заработанного опыта. НИКОГДА, НИКОГДА, НИКОГДА. Если только вы не делаете очень ненужный прототип. Я живу с непростительными наборами данных, летающими повсюду. Поначалу они кажутся хорошей идеей (эй, это облегчающая разработка!!), но они становятся помехой, как только вы пытаетесь поддерживать или создавать/расширять их. Если вы потратите еще 30 минут на написание классов для данных, это окупится почти мгновенно (модульное тестирование кого-нибудь?).

Ответ №3:

Если сохраненный процесс возвращает несколько наборов, используйте DataReader.NextResult для перехода к следующему фрагменту данных. Таким образом, вы можете получить все свои данные, загрузить их в свои объекты и как можно скорее закрыть считыватель. Это будет самый быстрый способ получить ваши данные.

Комментарии:

1. Фантастика, спасибо! Мне всегда было интересно, сможешь ли ты это сделать — должно быть, я вела уединенную жизнь!

Ответ №4:

Видя, что ни один ответ еще не отмечен, хотя уже есть множество хороших ответов, я подумал, что тоже добавлю два бита.

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

Некоторые люди говорят, что не стоит отправлять DataReader по слоям. Лично я не вижу в этом проблемы, поскольку «DbDataReader» технически не привязан (или не должен быть привязан) к базе данных. То есть вы можете создать экземпляр DbDataReader без необходимости в базе данных.

Я делаю это по следующим причинам: Часто (в веб-приложении) вы генерируете либо Html, либо Xml, либо JSON, либо какое-либо другое преобразование ваших данных. Так зачем переходить от DaraReader к какому-то объекту POCO только для того, чтобы преобразовать его обратно в XML или JSON и отправить по проводу. Этот вид процесса обычно требует 3 преобразований и загрузки экземпляров объектов только для того, чтобы выбросить их почти мгновенно.

В некоторых ситуациях это нормально, а в других ничего не поделаешь. Мой уровень данных обычно использует два метода для каждой хранимой процедуры, которая у меня есть в системе. Один возвращает DbDataReader, а другой возвращает набор данных/DataTable. Методы, возвращающие DataSet/DataTable, вызывают метод, возвращающий DbDataReader, а затем используют либо метод «Load» DataTable, либо адаптер для заполнения набора данных. Иногда вам нужны наборы данных, потому что вам, вероятно, придется каким-то образом перепрофилировать данные или вам нужно запустить другой запрос, и если у вас включен MARS, вы не можете открыть DbDataReader и запустить другой запрос.

Теперь есть некоторые проблемы с использованием DbDataReader или DataSet/DataTable, и это, как правило, ясность кода, проверка времени компиляции и т.д. Вы можете использовать классы-оболочки для своего читателя данных, и на самом деле вы можете использовать свои читатели данных с их числом. Действительно классная возможность. Таким образом, вы не только получаете четкую типизацию и удобочитаемость кода, вы также получаете множество номеров!

Таким образом, класс может выглядеть так.

 public sealed class BlogItemDrw : BaseDbDataReaderWrapper
{
    public Int64 ItemId { get { return (Int64)DbDataReader[0]; } }
    public Int64 MemberId { get { return (Int64)DbDataReader[1]; } }
    public String ItemTitle { get { return (String)DbDataReader[2]; } }
    public String ItemDesc { get { if (DbDataReader[3] != DBNull.Value) return (String)DbDataReader[3]; else return default(String); } }
    public DateTime ItemPubdate { get { return (DateTime)DbDataReader[4]; } }
    public Int32 ItemCommentCnt { get { return (Int32)DbDataReader[5]; } }
    public Boolean ItemAllowComment { get { return (Boolean)DbDataReader[6]; } }
    public BlogItemDrw()
      :base()
    {
    }

    public BlogItemDrw(DbDataReader dbDataReader)
      :base(dbDataReader)
    {
    }
}
 

Оболочка для чтения данных

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

Вы можете использовать ту же технику для таблиц данных (генератор кода создает код), чтобы вы могли рассматривать их как строго типизированные таблицы данных без дополнительных затрат на то, что VS.NET обеспечивает из коробки.

Имейте в виду, что существует только один экземпляр класса-оболочки. Таким образом, вы не создаете сотни экземпляров класса только для того, чтобы выбросить его.

Комментарии:

1. Это очень интересный (и подробный) ответ.

2. Попробуйте перейти на следующий уровень и обернуть datareader блоком итератора, который возвращает an IEnumerable<IDataRecord> . Давайте сразу же используем результаты ваших запросов в linq.

3. Джоэл, если вы посмотрите на мой пост в блоге, вы увидите, что я тоже показываю, как это сделать :). На самом деле то, что я использую в своих проектах, идет на несколько шагов дальше, вы можете использовать тот же класс, что и POCO, чтобы получить список<T> (когда вам нужно) или использовать его в качестве оболочки.

Ответ №5:

Сопоставьте средство чтения данных с промежуточными объектами, а затем свяжите элементы управления с помощью этих объектов. В определенных обстоятельствах может быть нормально использовать наборы данных, но их немного и они находятся далеко друг от друга, когда у вас есть веские причины для «просто получения данных». Что бы вы ни делали, не передавайте средство чтения данных своим элементам управления для привязки (не то, чтобы вы говорили, что рассматривали это).

Моим личным предпочтением было бы использовать ORM, но если вы собираетесь передать свой доступ к данным, я думаю, что вам следует предпочесть сопоставление считывателей данных с объектами, а не использование наборов данных. Использование .NextResult в качестве способа ограничить себя от многократного попадания в базу данных-это палка о двух концах, однако выбирайте с умом. Вы обнаружите, что повторяетесь, если попытаетесь создать процессы, которые всегда будут захватывать именно то, что вам нужно, используя только один вызов базы данных. Если ваше приложение состоит всего из нескольких страниц, это, вероятно, нормально, но ситуация может быстро выйти из-под контроля. Лично я бы предпочел иметь один процесс для каждого типа объекта, а затем несколько раз обращаться к базе данных (по одному разу для каждого типа объекта), чтобы максимизировать удобство обслуживания. Именно здесь ORM сияет, потому что хороший будет генерировать Sql, который в большинстве случаев даст вам именно то, что вы хотите, одним вызовом.

Комментарии:

1. Я бы подумал, что чрезмерные накладные расходы на использование наборов данных и таблиц данных всегда сделают их плохим выбором для чего угодно, но только не для тех случаев, когда вам хочется полениться 😉 aspnet. 4guysfromrolla.com/articles/050405-1.aspx

2. В целом я согласился, поэтому я обычно сопоставляю промежуточные объекты. Я бы предпочел накладные расходы на сопоставление с DTO, чем поддерживать соединение открытым при привязке к нескольким элементам управления. Кроме того, передача считывателей данных через уровни является злом, поэтому общие рекомендации по проектированию запрещают привязку к считывателю данных

3. Чтобы уточнить: Мой порядок предпочтений: 1) Привязка к объектам 2) Привязка к набору данных/таблице/адаптеру 3) Привязка непосредственно к datareader (не делайте этого!).

Ответ №6:

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

Ответ №7:

Практически в любой ситуации DataReader s являются лучшим решением для чтения из базы данных. DataReader s быстрее и требуют меньше памяти, чем DataTable s или DataSet s.

Кроме того, DataSet s часто может приводить к ситуациям, в которых модель OO нарушается. Не очень объектно-ориентировано передавать реляционные данные/схемы вместо объектов, которые знают, как манипулировать этими данными.

Поэтому, по соображениям расширяемости, масштабируемости, модульности и производительности, всегда используйте DataReader s, если вы считаете себя настоящим программистом™ 😉

Проверьте ссылки на факты и обсуждение этих двух вопросов на практике и в теории.

Ответ №8:

Независимо от того, извлекаете ли вы один набор результатов или несколько наборов результатов, консенсус, по-видимому, заключается в использовании DataReader вместо набора данных.

Что касается того, следует ли вам когда-либо беспокоиться о нескольких наборах результатов, мудрость заключается в том, что вам не следует этого делать, но я могу представить разумный класс исключений из этого правила: (тесно) связанные наборы результатов. Вы, конечно, не хотите добавлять запрос для возврата одного и того же набора вариантов для раскрывающегося списка, который повторяется на сотнях или даже десятках страниц вашего приложения, но несколько наборов, используемых в узком диапазоне, могут быть разумно объединены. В качестве примера, в настоящее время я создаю страницу для отображения нескольких наборов «несоответствий» для пакета данных ETL. Ни один из этих запросов, скорее всего, не будет использоваться в других местах, поэтому было бы удобно инкапсулировать их в виде единого запроса «получить расхождения». С другой стороны, лучшая производительность при этом может быть незначительной по сравнению с обработкой естественной архитектуры набора результатов с одним запросом для вашего ORM или кода доступа к данным вручную.

Ответ №9:

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

Лично с несколькими выпадающими списками я обычно выбираю отдельные фрагменты данных, чтобы получить их, а не, скажем, хранимую процедуру, которая возвращает 5 наборов результатов.

Ответ №10:

Взгляните на адаптеры таблиц, доступные в .NET 2.0 и выше. Что они делают, так это придают вам силу строго типизированной таблицы данных и позволяют сопоставить с ней метод заполнения, который будет использовать средство чтения данных для ее загрузки. Вашим методом заполнения могут быть существующие хранимые процедуры, ваш собственный SQL AdHoc или даже позволить мастеру создать для вас AdHod или хранимую процедуру.

Вы можете найти это, запустив новый объект набора данных XSD в своем проекте. Для таблиц, которые используются не только для поиска, вы также можете сопоставить методы вставки/обновления/удаления с адаптером таблиц.