Эффективное разбиение на страницы больших объемов данных (C#)

Эффективное разбиение на страницы больших объемов данных (C#)

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

Введение

Как обсуждалось в предыдущем учебном курсе, разбиение по страницам можно реализовать одним из двух способов:

  • Разбиение по страницам по умолчанию можно реализовать, просто установив флажок Включить разбиение по страницам в смарт-теге элемента управления данными. Однако при просмотре страницы данных ObjectDataSource извлекает все записи, несмотря на то, что на странице отображаются только их подмножество.
  • Пользовательское разбиение на страницы улучшает производительность разбиения по страницам по умолчанию, получая из базы данных только те записи, которые должны отображаться для конкретной странице данных, запрошенных пользователем. Однако пользовательское разбиение по страницам требует немного больше усилий, чем разбиение по умолчанию

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

Задача пользовательского разбиения по страницам — возможность написать запрос, возвращающий точный набор записей, необходимых для конкретной страницы данных. К счастью, Microsoft SQL Server 2005 предоставляет новое ключевое слово для ранжирования результатов, что позволяет нам создавать запросы, которые эффективно извлекают подмножество записей. В этом учебнике мы увидим, как использовать это новое ключевое слово SQL Server 2005 для реализации пользовательского разбиения на страницы в элементе управления GridView. Несмотря на то, что пользовательский интерфейс для пользовательского разбиения по страницам аналогичен постраничному разбиению по умолчанию, переход от одной страницы к другой с помощью пользовательского разбиения по страницам может быть несколько порядков быстрее, чем разбиение по страницам

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

Шаг 1. Основные сведения о пользовательском процессе разбиения на страницы

При разбиении по страницам данных точные записи, отображаемые на странице, зависят от запрашиваемой страницы данных и количества записей, отображаемых на странице. Например, представьте, что нам нужно пролистать продукты 81, отображая 10 продуктов на странице. При просмотре первой страницы нам нужно, чтобы продукты с 1 по 10 были При просмотре второй страницы мы будем заинтересованы в продуктах с 11 по 20 и т. д.

Есть три переменные, которые определяют, какие записи необходимо получить, и как должен быть визуализирован интерфейс разбиения по страницам:

  • Начать индекс строки индекс первой строки на странице отображаемых данных; Этот индекс можно вычислить, умножив индекс страницы по записям, отображаемым на страницу, и добавив ее. Например, при разбиении по записям 10 за раз для первой страницы (с индексом страницы 0) Индекс начальной строки равен 0 * 10 + 1, или 1; для второй страницы (чей индекс страницы равен 1), Индекс начальной строки равен 1 * 10 + 1 или 11.
  • Максимальное число строк , отображаемых на страницу по максимальному числу записей. Эта переменная называется максимальным числом строк, так как для последней страницы может быть меньше записей, чем размер страницы. Например, при разбиении по страницам 10 записей продуктов 81 на странице девятая и последняя страницы будут содержать только одну запись. Ни одна страница не будет показывать больше записей, чем максимальное значение строк.
  • Общее количество записей общее число страниц, на которые размещается страница. Хотя эта переменная не требуется для определения того, какие записи следует получить для данной страницы, она определяет интерфейс разбиения на себя. Например, если имеется 81 продуктов, то интерфейс разбиения по страницам будет отображать девять номеров страниц в пользовательском интерфейсе разбиения на страницы.

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

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

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

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

Шаг 2. возвращение общего количества записей, передаваемых по страницам

Прежде чем исследовать, как получить точное подмножество записей для отображаемой страницы, давайте сначала посмотрим, как вернуть общее количество записей, на которые размещается страница. Эти сведения необходимы для правильной настройки интерфейса пользователя с разбиением на страницы. Общее число записей, возвращаемых определенным запросом SQL, можно получить с помощью COUNT агрегатной функции. Например, чтобы определить общее число записей в Products таблице, можно использовать следующий запрос:

Добавим в DAL метод, возвращающий эту информацию. В частности, мы создадим метод DAL с именем TotalNumberOfProducts() , который выполняет приведенную SELECT выше инструкцию.

Начните с открытия Northwind.xsd файла типизированного набора данных в App_Code/DAL папке. Затем щелкните правой кнопкой мыши ProductsTableAdapter в конструкторе и выберите команду Добавить запрос. Как мы видели в предыдущих учебных курсах, это позволит нам добавить в DAL новый метод, который при вызове будет выполнять определенную инструкцию или хранимую процедуру SQL. Как и в случае с нашими методами TableAdapter в предыдущих руководствах, для этого нужно использовать специальный оператор SQL.

Рис. 1. Использование специального оператора SQL

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

Рис. 2. Настройка запроса для использования инструкции SELECT, возвращающей одиночное значение

После указания используемого типа запроса необходимо указать запрос.

Рис. 3. Использование запроса SELECT COUNT ( * ) из продуктов

Наконец, укажите имя метода. Как было упомянуто выше, давайте будем использовать TotalNumberOfProducts .

Рис. 4. имя метода DAL тоталнумберофпродуктс

После нажатия кнопки Готово мастер добавит TotalNumberOfProducts метод в DAL. Скалярные методы, возвращающие DAL, возвращают типы, допускающие значение null, в случае, если результат запроса SQL равен NULL . COUNT Однако наш запрос всегда будет возвращать значение, отличное от NULL значения, независимо от того, что метод DAL возвращает целое число, допускающее значение null.

В дополнение к методу DAL нам также нужен метод в BLL. Откройте ProductsBLL файл класса и добавьте TotalNumberOfProducts метод, который просто вызывает метод DAL s TotalNumberOfProducts :

Метод DAL s TotalNumberOfProducts возвращает целое число, допускающее значение null, но мы создали ProductsBLL метод класса s, TotalNumberOfProducts чтобы он возвращал стандартное целое число. Поэтому необходимо, чтобы ProductsBLL метод класса s TotalNumberOfProducts возвращал часть значения обнуляемого целого числа, возвращенного методом DAL s TotalNumberOfProducts . Вызов GetValueOrDefault() возвращает значение обнуляемого целого числа, если оно существует; если значение, допускающее значения NULL null , равно, то возвращается целочисленное значение по умолчанию 0.

Шаг 3. возврат точного подмножества записей

Следующей задачей является создание методов DAL и BLL, принимающих переменные начальной строки и максимального числа строк, которые обсуждались ранее, и возвращают соответствующие записи. Прежде чем это сделать, давайте взглянем на необходимый скрипт SQL. Проблема заключается в том, что мы должны иметь возможность эффективно назначать индекс каждой строке во всех результатах, чтобы мы могли возвращать только те записи, начиная с индекса начальной строки (и до максимального количества записей).

Это не является проблемой, если в таблице базы данных уже есть столбец, который служит индексом строки. На первый взгляд может показаться, что Products поле Table s ProductID достаточно, так как первый продукт имеет значение ProductID 1, второй — 2 и т. д. Однако удаление продукта оставляет разрыв в последовательности, отменяя этот подход.

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

Использование SQL Server 2005 s ROW_NUMBER() Ключевое слово New для SQL Server 2005, ROW_NUMBER() ключевое слово связывает ранжирование с каждой возвращаемой записью на основе определенного порядка. Этот рейтинг можно использовать в качестве индекса строки для каждой строки.

**Использование табличной переменной и SET ROWCOUNT ** С помощью SET ROWCOUNT инструкции SQL Server s можно указать, сколько записей будет обрабатывать запрос перед завершением работы. табличные переменные — это локальные переменные T-SQL, которые могут содержать табличные данные, в аналогах с временными таблицами. Этот подход работает одинаково хорошо с Microsoft SQL Server 2005 и SQL Server 2000 (в то время как ROW_NUMBER() подход работает только с SQL Server 2005).

Идея состоит в том, чтобы создать табличную переменную со IDENTITY столбцом и столбцами для первичных ключей таблицы, данные на которых разгружаются по страницам. Далее содержимое таблицы, данные из которой догружаются по страницам, копируется в табличную переменную, тем самым связывая индекс последовательной строки (через IDENTITY столбец) для каждой записи в таблице. После заполнения табличной переменной SELECT инструкция в табличной переменной, присоединенной к базовой таблице, может быть выполнена для извлечения определенных записей. SET ROWCOUNT Инструкция используется для разумного ограничения количества записей, которые необходимо выгрузить в табличную переменную.

Эффективность этого подхода зависит от запрашиваемого номера страницы, так как этому SET ROWCOUNT значению присваивается значение индекса начальной строки плюс максимальное число строк. При разбиении на страницы с низкой нумерацией, например первые несколько страниц данных, этот подход очень эффективен. Однако при извлечении страницы рядом с ней используется производительность по умолчанию, аналогичная разбиению по страницам.

В этом руководстве реализуется пользовательский разбиение на страницы с помощью ROW_NUMBER() ключевого слова. Дополнительные сведения об использовании табличной переменной и SET ROWCOUNT методики см. в разделе более эффективный метод разбиения по страницам больших результирующих наборов.

ROW_NUMBER() Ключевое слово связывает ранжирование с каждой записью, возвращенной в определенном порядке, с помощью следующего синтаксиса:

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

На рис. 5 показаны результаты этого запроса при выполнении в окне запроса в Visual Studio. Обратите внимание, что продукты упорядочиваются по цене вместе с рейтингом цены для каждой строки.

Рис. 5. рейтинг цены включен для каждой возвращенной записи

ROW_NUMBER() — Это лишь одна из многих новых функций ранжирования, доступных в SQL Server 2005. Дополнительные сведения о ROW_NUMBER() , а также другие ранжирующие функции см. в статье Получение ранжированных результатов с помощью Microsoft SQL Server 2005.

При ранжировании результатов по указанному ORDER BY столбцу в OVER предложении ( UnitPrice в приведенном выше примере) SQL Server должны сортировать результаты. Это быстрая операция, если имеется кластеризованный индекс по столбцам, в котором упорядочиваются результаты, или если присутствует индекс, который в противном случае может быть более затратным. Чтобы повысить производительность для достаточно больших запросов, рассмотрите возможность добавления некластеризованного индекса для столбца, по которому упорядочиваются результаты. Более подробные сведения о производительности см. в разделе ранжирующие функции и производительность в SQL Server 2005 .

Сведения о ранжировании, возвращаемые, ROW_NUMBER() нельзя использовать в WHERE предложении напрямую. Однако производную таблицу можно использовать для возврата ROW_NUMBER() результата, который затем может появиться в WHERE предложении. Например, следующий запрос использует производную таблицу для возврата столбцов ProductName и UnitPrice вместе с ROW_NUMBER() результатом, а затем использует WHERE предложение для возврата только тех продуктов, ранг цен которых составляет от 11 до 20:

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

Как будет показано далее в этом учебнике, объект, StartRowIndex предоставленный ObjectDataSource, индексируется начиная с нуля, а ROW_NUMBER() значение, возвращаемое SQL Server 2005, индексируется начиная с 1. Таким образом, WHERE предложение возвращает те записи, PriceRank в которых строго больше StartRowIndex и меньше или равно StartRowIndex + MaximumRows .

Теперь, когда мы рассмотрели, как ROW_NUMBER() можно использовать для получения определенной страницы данных с учетом значений индекса начальной и максимальной строк, нам нужно реализовать эту логику как методы DAL и BLL.

При создании этого запроса необходимо выбрать упорядочивание, по которому будут ранжированы результаты. Позвольте s Сортировать продукты по именам в алфавитном порядке. Это означает, что при реализации пользовательского разбиения по страницам в этом учебнике не будет возможности создать пользовательский отчет с разбивкой на страницы, чем также можно будет отсортировать. Тем не менее, в следующем учебном курсе мы увидим, как можно предоставить такую функциональность.

В предыдущем разделе мы создали метод DAL в качестве специального оператора SQL. К сожалению, средство синтаксического анализа T-SQL в Visual Studio, используемое мастером TableAdapter, не похоже на OVER синтаксис, используемый ROW_NUMBER() функцией. Поэтому необходимо создать этот метод DAL в качестве хранимой процедуры. Выберите обозреватель сервера в меню Вид (или нажмите клавиши CTRL + ALT + S) и разверните NORTHWND.MDF узел. Чтобы добавить новую хранимую процедуру, щелкните правой кнопкой мыши узел Хранимые процедуры и выберите команду Добавить новую хранимую процедуру (см. рис. 6).

Рис. 6. Добавление новой хранимой процедуры для разбиения по страницам продуктов

Эта хранимая процедура должна принимать два целочисленных входных параметра — @startRowIndex и @maximumRows использовать ROW_NUMBER() функцию, упорядоченную по ProductName полю, возвращая только те строки, которые больше заданного значения @startRowIndex и меньше или равны @startRowIndex + @maximumRow . Введите следующий скрипт в новую хранимую процедуру, а затем щелкните значок Сохранить, чтобы добавить хранимую процедуру в базу данных.

После создания хранимой процедуры уделите время ее протестировать. Щелкните правой кнопкой мыши GetProductsPaged имя хранимой процедуры в обозреватель сервера и выберите пункт Выполнить. В Visual Studio будет предложено ввести входные параметры @startRowIndex и @maximumRow s (см. рис. 7). Попробуйте использовать другие значения и проверьте результаты.

@startRowIndex @maximumRows параметров и" />

Рис. 7. Ввод значения для @startRowIndex параметров и @maximumRows

После выбора значений входных параметров в окне вывода отобразятся результаты. На рис. 8 показаны результаты при передаче в 10 для @startRowIndex параметров и @maximumRows .

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

После создания этой хранимой процедуры мы повторно готовы к созданию ProductsTableAdapter метода. Откройте Northwind.xsd типизированный набор данных, щелкните правой кнопкой мыши в ProductsTableAdapter и выберите пункт Добавить запрос. Вместо создания запроса с помощью специальной инструкции SQL создайте ее с помощью существующей хранимой процедуры.

Рис. 9. Создание метода DAL с помощью существующей хранимой процедуры

Далее будет предложено выбрать хранимую процедуру для вызова. Выберите GetProductsPaged хранимую процедуру из раскрывающегося списка.

Рис. 10. Выбор хранимой процедуры GetProductsPaged из раскрывающегося списка

Затем на следующем экране запрашивается тип данных, возвращаемых хранимой процедурой: табличные данные, одно значение или значение No. Так как GetProductsPaged хранимая процедура может возвращать несколько записей, укажите, что она возвращает табличные данные.

Рис. 11. Указание того, что хранимая процедура возвращает табличные данные

Наконец, укажите имена методов, которые необходимо создать. Как и в предыдущих учебных курсах, вы можете создавать методы, используя как заполнение DataTable, так и возвратить таблицу данных. Назовите первый метод FillPaged и второй GetProductsPaged .

Рис. 12. Назовите методы Филлпажед и GetProductsPaged

Помимо создания метода DAL для возврата определенной страницы продуктов, нам также нужно предоставить такую функциональность в BLL. Как и метод DAL, метод BLL GetProductsPaged должен принимать два целочисленных значения для указания индекса начальной и максимальной строк, а также должен возвращать только те записи, которые попадают в указанный диапазон. Создайте такой метод BLL в классе ProductsBLL, который просто вызывает метод DAL s GetProductsPaged, вот так:

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

Шаг 4. Настройка элемента управления ObjectDataSource для использования пользовательского разбиения на страницы

С помощью методов BLL и DAL для получения доступа к определенному подмножеству записей мы повторно готовы к созданию элемента управления GridView, который просматривает базовые записи, используя пользовательское разбиение по страницам. Сначала откройте EfficientPaging.aspx страницу в PagingAndSorting папке, добавьте GridView на страницу и настройте ее для использования нового элемента управления ObjectDataSource. В прошлых учебных курсах мы часто настроили ObjectDataSource на использование ProductsBLL метода Class s GetProducts . Но на этот раз мы хотим использовать GetProductsPaged вместо этого метод, так как GetProducts метод возвращает все продукты в базе данных, тогда как GetProductsPaged возвращает только определенное подмножество записей.

Рис. 13. Настройка ObjectDataSource для использования метода GetProductsPaged класса ProductsBLL

Так как мы повторно создаем GridView, доступное только для чтения, задавайте в раскрывающийся список метод на вкладках Вставка, обновление и удаление значение (нет).

Затем мастер ObjectDataSource запрашивает источники GetProductsPaged значений методов s startRowIndex и maximumRows input Parameters. Эти входные параметры фактически задаются в элементе управления GridView автоматически, поэтому просто оставьте исходное значение None и нажмите кнопку Готово.

Рис. 14. Оставьте источники входных параметров как нет

После завершения работы мастера ObjectDataSource элемент GridView будет содержать BoundField или CheckBoxField для каждого поля данных продукта. Вы можете адаптировать внешний вид GridView s по своему усмотрению. Я решил отобразить только. ProductName CategoryName SupplierName QuantityPerUnit и UnitPrice BoundFields. Кроме того, настройте GridView для поддержки разбиения на страницы, установив флажок Включить разбиение по страницам в его смарт-теге. После этих изменений декларативная разметка GridView и ObjectDataSource должна выглядеть следующим образом:

Однако если вы посещаете страницу в браузере, GridView не сможет найти.

Рис. 15. элемент управления GridView не отображается

Элемент GridView отсутствует, поскольку ObjectDataSource в настоящий момент использует 0 в качестве значений для обоих GetProductsPaged startRowIndex maximumRows входных параметров и. Таким образом, результирующий запрос SQL не возвращает никаких записей, поэтому GridView не отображается.

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

  1. Присвойте EnablePaging свойству true ObjectDataSource s значение this, указывающее элементу ObjectDataSource, что он должен передаваться в SelectMethod два дополнительных параметра: один для указания индекса начальной строки ( StartRowIndexParameterName ), а другой — для указания максимального числа строк ( MaximumRowsParameterName ).
  2. Задайте для элементов ObjectDataSource StartRowIndexParameterName и MaximumRowsParameterName Properties соответствующие StartRowIndexParameterName Свойства и MaximumRowsParameterName указывают имена входных параметров, передаваемых в SelectMethod для настраиваемого разбиения по страницам. По умолчанию эти имена параметров являются startIndexRow и maximumRows , поэтому при создании GetProductsPaged метода в BLL я использовал эти значения для входных параметров. Если выбрано использование различных имен параметров для метода BLL s GetProductsPaged startIndex , например maxRows , и, например, необходимо соответствующим образом задать элементы ObjectDataSource StartRowIndexParameterName и MaximumRowsParameterName Свойства (например, startIndex для StartRowIndexParameterName и maxRows для MaximumRowsParameterName ).
  3. Присвойте SelectCountMethod свойству ObjectDataSource s имя метода, возвращающего общее количество записей, передаваемых по страницам ( TotalNumberOfProducts ) , чтобы ProductsBLL метод класса s TotalNumberOfProducts возвращал общее количество записей, на которые помещается страница, с помощью метода DAL, выполняющего SELECT COUNT(*) FROM Products запрос. Эти сведения необходимы ObjectDataSource для правильного отображения интерфейса разбиения по страницам.
  4. Удалите startRowIndex элементы и maximumRows <asp:Parameter> из декларативной разметки ObjectDataSource s при настройке ObjectDataSource с помощью мастера Visual Studio автоматически добавил два <asp:Parameter> элемента для GetProductsPaged входных параметров метода. Если задано значение EnablePaging true , эти параметры будут передаваться автоматически. Если они также появляются в декларативном синтаксисе, то ObjectDataSource попытается передать четыре параметра GetProductsPaged методу и два параметра в TotalNumberOfProducts метод. Если вы забыли удалить эти <asp:Parameter> элементы, при посещении страницы в браузере вы получите следующее сообщение об ошибке: ObjectDataSource ' ObjectDataSource1 ' не может найти неуниверсальный метод ' тоталнумберофпродуктс ' с параметрами: StartRowIndex, maximumRows.

После внесения этих изменений декларативный синтаксис ObjectDataSource s должен выглядеть следующим образом:

Обратите внимание, что EnablePaging Свойства и были SelectCountMethod установлены и <asp:Parameter> элементы были удалены. На рис. 16 показан снимок экрана окно свойств после внесения этих изменений.

Рисунок 16. Настройка элемента управления ObjectDataSource с помощью пользовательского разбиения на страницы

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

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

При использовании пользовательского разбиения на страницы значение счетчика страниц, возвращаемое ObjectDataSource, SelectCountMethod сохраняется в состоянии представления GridView. Другие переменные GridView. PageIndex EditIndex SelectedIndex , DataKeys коллекция и т. д. хранятся в состоянии элемента управления, которое сохраняется независимо от значения EnableViewState Свойства GridView. Так как PageCount значение сохраняется между обратными передачами с помощью состояния представления, при использовании интерфейса разбиения по страницам, включающего ссылку для перехода на последнюю страницу, необходимо включить состояние представления GridView. (Если интерфейс разбиения по страницам не содержит прямую ссылку на последнюю страницу, то можно отключить состояние просмотра.)

Щелчок последней ссылки на страницу вызывает обратную передачу и указывает GridView обновить PageIndex свойство. Если щелкнуть ссылку на последнюю страницу, GridView присваивает PageIndex свойству значение, которое меньше его PageCount Свойства. При отключенном состоянии представления PageCount значение теряется во всех обратных передачах, а PageIndex вместо этого назначается максимальное целое значение. Затем GridView пытается определить начальный индекс строки путем умножения PageSize PageCount свойств и. Это приводит к возникновению, OverflowException так как размер продукта превышает максимально допустимый.

Реализация пользовательского разбиения по страницам и сортировки

Наша текущая реализация пользовательского разбиения по страницам требует, чтобы порядок, в котором данные выдаются с разбивкой на страницы, был задан статически при создании GetProductsPaged хранимой процедуры. Однако вы могли заметить, что смарт-тег GridView s содержит флажок Включить сортировку в дополнение к параметру Включить разбиение на страницы. К сожалению, Добавление поддержки сортировки в GridView с текущей реализацией пользовательского разбиения по страницам сортирует только записи на просматриваемой на данный момент странице данных. Например, если вы настраиваете GridView для поддержки разбиения на страницы, а затем, когда просматриваете первую страницу данных, сортировать по названию продукта в убывающем порядке, порядок продуктов на странице 1 будет реверсирован. Как показано на рис. 18, в качестве первого продукта при сортировке в противоположном алфавитном порядке, например Карнарвон, не учитываются 71 других продуктов, которые выводятся после Карнарвон Tiger по алфавиту. в сортировке учитываются только записи на первой странице.

Рис. 18. Сортировка только данных, отображаемых на текущей странице (щелкните, чтобы просмотреть изображение с полным размером)

Сортировка применяется только к текущей странице данных, так как сортировка происходит после извлечения данных из метода BLL s GetProductsPaged и этот метод возвращает только те записи, которые относятся к конкретной странице. Чтобы правильно реализовать сортировку, необходимо передать выражение сортировки GetProductsPaged методу, чтобы данные могли быть соответствующим образом упорядочены перед возвратом конкретной страницы данных. Мы посмотрим, как это сделать в следующем руководстве.

Реализация пользовательского разбиения по страницам и удаление

При включении функции удаления в элементе управления GridView, данные которого выводятся с помощью настраиваемых методов разбиения по страницам, вы обнаружите, что при удалении последней записи с последней страницы GridView исчезает, а не будет уменьшаться в GridView PageIndex . Чтобы воспроизвести эту ошибку, включите удаление для учебника только что созданного. Перейдите на последнюю страницу (стр. 9), где вы должны увидеть один продукт, так как мы подкачкой продукты 81, 10 продуктов за раз. Удалите этот продукт.

После удаления последнего продукта GridView будет автоматически переходить к восьмой странице, и такие функции будут работать с разбиением по страницам по умолчанию. Однако при использовании пользовательского разбиения по страницам после удаления последнего продукта на последней странице GridView просто исчезает с экрана. Точная Причина, по которой это происходит, немного выходит за рамки данного учебника. см. статью Удаление последней записи на последней странице из GridView с настраиваемым разбиением на страницы для сведений низкого уровня, как в источник этой проблемы. В сводке это происходит из-за следующей последовательности действий, выполняемых элементом GridView при нажатии кнопки "Удалить":

  1. Удаление записи
  2. Получение соответствующих записей, отображаемых для указанных PageIndex и PageSize
  3. Убедитесь, что параметр не PageIndex превышает количество страниц данных в источнике данных; если это так, автоматически уменьшите свойство GridView s PageIndex
  4. Свяжите соответствующую страницу данных с элементом GridView, используя записи, полученные на шаге 2.

Проблема обусловлена тем, что на шаге 2, который PageIndex использовался при извлечении отображаемых записей, по-прежнему является PageIndex последней страницей, единственная запись которой была только что удалена. Поэтому на шаге 2 никакие записи не возвращаются, так как последняя страница данных больше не содержит никаких записей. Затем в шаге 3 в элементе управления GridView понимается, что его PageIndex свойство больше, чем общее число страниц в источнике данных (так как мы удалили последнюю запись на последней странице), и, таким образом, уменьшаем ее PageIndex свойство. На шаге 4 GridView пытается привязать себя к данным, полученным на шаге 2. Однако на шаге 2 не было возвращено ни одной записи, поэтому в результате возвращается пустой элемент GridView. При использовании разбиения по страницам по умолчанию эта проблема не имеет значения, так как на шаге 2 все записи извлекаются из источника данных.

Чтобы устранить эту проблему, у нас есть два варианта. Первый заключается в создании обработчика событий для RowDeleted обработчика событий GridView s, который определяет, сколько записей отображалось на странице, которая была только что удалена. Если была только одна запись, то только что удаленная запись должна была быть последней, и нам нужно уменьшить GridView s PageIndex . Конечно, мы хотим обновить только в случае, PageIndex Если операция удаления фактически была успешной, что можно определить, убедившись, что e.Exception свойство имеет значение null .

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

Альтернативным решением является создание обработчика событий для события ObjectDataSource s RowDeleted и присвоение AffectedRows свойству значения 1. После удаления записи в шаге 1 (но перед повторной извлечением данных на шаге 2) GridView обновляет PageIndex свойство, если одна или несколько строк были затронуты операцией. Однако AffectedRows свойство не задается элементом ObjectDataSource и поэтому этот шаг опускается. Один из способов выполнения этого шага — Ручное задание свойства в AffectedRows случае успешного завершения операции удаления. Это можно сделать с помощью кода, подобного приведенному ниже.

Код для обоих обработчиков событий можно найти в классе кода программной части EfficientPaging.aspx примера.

Сравнение производительности по умолчанию и пользовательского разбиения на страницы

Поскольку пользовательское разбиение по страницам извлекает только необходимые записи, в то время как разбиение по страницам по умолчанию возвращает все записи для каждой просматриваемой страницы, то ясно, что настраиваемое разбиение по страницам более эффективно, чем разбиение Но насколько эффективнее пользовательское разбиение на страницы? Какой выигрыш в производительности можно увидеть, перемещаясь с разбиения по страницам по умолчанию на пользовательское разбиение?

К сожалению, нет ни одного размера, удовлетворяющего всем ответам. Выигрыш в производительности зависит от ряда факторов, наиболее заметных двух из которых является число страниц, на которых выполняется разгрузка, и нагрузку на сервер базы данных и каналы связи между веб-сервером и сервером базы данных. Для небольших таблиц, имеющих всего несколько десятков записей, разница в производительности может быть незначительной. Однако в больших таблицах с тысячами сотен тысяч строк разница в производительности имеет значение акутом.

В статье «мой пользовательское подкачка» в ASP.NET 2,0 с SQL Server 2005содержится несколько тестов производительности, которые я выполнял, чтобы демонстрировать различия в производительности между этими двумя методами разбиения по страницам в таблице базы данных с 50 000 записей. В этих тестах я рассматривал время выполнения запроса на уровне SQL Server (с помощью SQL Profiler) и на странице ASP.NET с помощью функций трассировки ASP.NET. Помните, что эти тесты были запущены в моем окне разработки с одним активным пользователем, и поэтому являются неинженерными и не воспроизводят типичные шаблоны нагрузки веб-сайтов. Независимо от этого результаты показывают относительные различия времени выполнения по умолчанию и пользовательского разбиения на страницы при работе с достаточно большим объемом данных.

СР. Длительность (в секундах) Reads Профайлер SQL по умолчанию для разбиения на страницы 1,411 383 Пользовательское разбиение по страницам SQL Profiler 0,002 29 Трассировка ASP.NET по умолчанию для подкачки 2,379 Н/Д ASP.NET трассировка пользовательской подкачки 0,029 Н/Д

Как видите, получение определенной страницы данных требует 354 меньшего числа операций чтения в среднем и завершено в доли времени. На странице ASP.NET пользователь может подготовить страницу к просмотру в течение 1/100- го времени, которое было затрачено при использовании разбиения по страницам по умолчанию. Дополнительные сведения об этих результатах вместе с кодом и базе данных, которую можно загрузить для воспроизведения тестов в собственной среде, см. в моей статье .

Сводка

Разбиение по страницам по умолчанию — очень просто для реализации просто установите флажок Включить разбиение по страницам в смарт-теге Web Control s, но такая простота достигается за счет производительности. При использовании разбиения по страницам по умолчанию, когда пользователь запрашивает любую страницу данных, возвращаются все записи, несмотря на то, что может отображаться лишь небольшая часть этих записей. Для борьбы с этими издержками производительности ObjectDataSource предлагает альтернативный вариант подкачки.

В то время как пользовательское разбиение по страницам улучшает производительность по умолчанию, извлекая только те записи, которые должны быть отображены, они больше участвовали в реализации пользовательского разбиения на страницы. Во-первых, необходимо записать запрос, который правильно (и эффективно) обращается к конкретному подмножеству запрошенных записей. Это можно сделать несколькими способами. Мы проверили в этом учебнике использование новой функции SQL Server 2005 s ROW_NUMBER() для ранжирования результатов, а затем возвратить только те результаты, ранжирование которых попадает в указанный диапазон. Кроме того, необходимо добавить средства для определения общего количества записей, на которые размещается страница. После создания этих методов DAL и BLL также необходимо настроить ObjectDataSource таким образом, чтобы он мог определить, сколько всего записей размещается по страницам, и может правильно передать значения индекса начальной и максимальной строк в BLL.

📎📎📎📎📎📎📎📎📎📎