Efektywne stronicowanie dużych ilości danych (C#)

Autor : Scott Mitchell

Pobierz plik PDF

Domyślna opcja stronicowania kontrolki prezentacji danych jest nieodpowiednia podczas pracy z dużymi ilościami danych, ponieważ jego podstawowa kontrola źródła danych pobiera wszystkie rekordy, mimo że wyświetlany jest tylko podzbiór danych. W takich okolicznościach musimy zwrócić się do niestandardowego stronicowania.

Wprowadzenie

Jak omówiono w poprzednim samouczku, stronicowanie można zaimplementować na jeden z dwóch sposobów:

  • Domyślne stronicowanie można zaimplementować, zaznaczając opcję Włącz stronicowanie w tagu inteligentnym kontrolki sieci Web danych; jednak podczas wyświetlania strony danych źródło ObjectDataSource pobiera wszystkie rekordy, mimo że na stronie są wyświetlane tylko podzbiór danych.
  • Stronicowanie niestandardowe poprawia wydajność domyślnego stronicowania przez pobieranie tylko tych rekordów z bazy danych, które muszą być wyświetlane dla określonej strony danych żądanych przez użytkownika; jednak stronicowanie niestandardowe wymaga nieco większego nakładu pracy w implementacji niż domyślne stronicowanie

Ze względu na łatwość implementacji wystarczy zaznaczyć pole wyboru i wszystko gotowe! domyślne stronicowanie jest atrakcyjną opcją. Jednak jego naiwne podejście do pobierania wszystkich rekordów sprawia, że jest to nieprawdopodobny wybór podczas stronicowania przez wystarczająco duże ilości danych lub witryn z wieloma współbieżnymi użytkownikami. W takich okolicznościach musimy zwrócić się do niestandardowego stronicowania, aby zapewnić dynamiczny system.

Wyzwanie związane z stronicowaniem niestandardowym polega na możliwości zapisania zapytania, które zwraca dokładny zestaw rekordów potrzebnych dla określonej strony danych. Na szczęście firma Microsoft SQL Server 2005 udostępnia nowe słowo kluczowe dla wyników klasyfikacji, co pozwala nam napisać zapytanie, które może efektywnie pobrać odpowiedni podzbiór rekordów. W tym samouczku zobaczymy, jak za pomocą tego nowego słowa kluczowego SQL Server 2005 zaimplementować niestandardowe stronicowanie w kontrolce GridView. Chociaż interfejs użytkownika dla niestandardowego stronicowania jest identyczny z tym w przypadku domyślnego stronicowania, przechodzenie z jednej strony do następnej przy użyciu niestandardowego stronicowania może być o kilka rzędów wielkości szybciej niż domyślne stronicowanie.

Uwaga

Dokładny wzrost wydajności wynikający z niestandardowego stronicowania zależy od całkowitej liczby rekordów stronicowanych oraz obciążenia umieszczanego na serwerze bazy danych. Na końcu tego samouczka przyjrzymy się niektórym trudnym metryce, które przedstawiają korzyści wynikające z wydajności uzyskanej za pośrednictwem niestandardowego stronicowania.

Krok 1. Omówienie niestandardowego procesu stronicowania

Podczas stronicowania danych dokładne rekordy wyświetlane na stronie zależą od żądanej strony danych i liczby wyświetlanych rekordów na stronę. Załóżmy na przykład, że chcemy stronicować 81 produktów, wyświetlając 10 produktów na stronę. Podczas wyświetlania pierwszej strony chcemy produktów od 1 do 10; podczas przeglądania drugiej strony bylibyśmy zainteresowani produktami od 11 do 20 itd.

Istnieją trzy zmienne, które określają, jakie rekordy należy pobrać, oraz sposób renderowania interfejsu stronicowania:

  • Rozpocznij indeks wiersza indeksu pierwszego wiersza na stronie danych do wyświetlenia; Ten indeks można obliczyć przez pomnożenie indeksu strony przez rekordy w celu wyświetlenia poszczególnych stron i dodania go. Na przykład w przypadku stronicowania rekordów 10 naraz dla pierwszej strony (której indeks strony wynosi 0), indeks początkowy wiersza to 0 * 10 + 1 lub 1; dla drugiej strony (której indeks strony to 1), indeks wiersza początkowego to 1 * 10 + 1 lub 11.
  • Maksymalna liczba wierszy maksymalna liczba rekordów do wyświetlenia na stronę. Ta zmienna jest określana jako maksymalna liczba wierszy, ponieważ dla ostatniej strony może być mniej rekordów zwracanych niż rozmiar strony. Na przykład w przypadku stronicowania przez 81 produktów 10 rekordów na stronę dziewiąta i końcowa strona będzie zawierać tylko jeden rekord. Żadna strona nie będzie jednak wyświetlać więcej rekordów niż wartość Maksymalna liczba wierszy.
  • Łączna liczba rekordów łączna liczba rekordów, przez które są stronicowane. Chociaż ta zmienna nie jest potrzebna do określenia, jakie rekordy mają być pobierane dla danej strony, decyduje o interfejsie stronicowania. Jeśli na przykład jest dostępnych 81 produktów, interfejs stronicowania wie, że wyświetla dziewięć numerów stron w interfejsie użytkownika stronicowania.

W przypadku domyślnego stronicowania indeks wiersza początkowego jest obliczany jako produkt indeksu strony i rozmiar strony oraz jeden, natomiast maksymalna liczba wierszy jest po prostu rozmiarem strony. Ponieważ domyślne stronicowanie pobiera wszystkie rekordy z bazy danych podczas renderowania dowolnej strony danych, indeks dla każdego wiersza jest znany, co powoduje przejście do wiersza Indeks wiersza początkowego jako proste zadanie. Ponadto łączna liczba rekordów jest łatwo dostępna, ponieważ jest to po prostu liczba rekordów w tabeli DataTable (lub dowolny obiekt używany do przechowywania wyników bazy danych).

Biorąc pod uwagę zmienne Indeks wiersza początkowego i Maksymalna liczba wierszy, niestandardowa implementacja stronicowania musi zwracać tylko dokładny podzbiór rekordów rozpoczynających się od indeksu wiersza początkowego i maksymalnie maksymalną liczbę wierszy po tym. Stronicowanie niestandardowe zapewnia dwa wyzwania:

  • Musimy być w stanie efektywnie skojarzyć indeks wierszy z każdym wierszem w całych danych, które są stronicowane, aby można było rozpocząć zwracanie rekordów w określonym indeksie wierszy początkowych
  • Musimy podać łączną liczbę stronicowanych rekordów

W następnych dwóch krokach przeanalizujemy skrypt SQL potrzebny do reagowania na te dwa wyzwania. Oprócz skryptu SQL będziemy również musieli zaimplementować metody w dal i BLL.

Krok 2. Zwracanie łącznej liczby rekordów stronicowanych

Zanim sprawdzimy, jak pobrać dokładny podzbiór rekordów dla wyświetlanej strony, najpierw przyjrzyjmy się, jak zwrócić łączną liczbę rekordów stronicowanych. Te informacje są potrzebne do prawidłowego skonfigurowania interfejsu użytkownika stronicowania. Łączna liczba rekordów zwracanych przez określone zapytanie SQL można uzyskać przy użyciu COUNT funkcji agregującej. Aby na przykład określić łączną liczbę rekordów w Products tabeli, możemy użyć następującego zapytania:

SELECT COUNT(*)
FROM Products

Dodajmy metodę do naszej nazwy DAL, która zwraca te informacje. W szczególności utworzymy metodę DAL o nazwie TotalNumberOfProducts() , która wykonuje instrukcję pokazaną SELECT powyżej.

Rozpocznij od otwarcia Northwind.xsd pliku Typed DataSet w folderze App_Code/DAL . Następnie kliknij prawym przyciskiem myszy element ProductsTableAdapter w Projektant i wybierz polecenie Dodaj zapytanie. Jak pokazano w poprzednich samouczkach, umożliwi to dodanie nowej metody do dal, która po wywołaniu wykona określoną instrukcję SQL lub procedurę składowaną. Podobnie jak w przypadku metod TableAdapter w poprzednich samouczkach, w tym przypadku należy użyć instrukcji ad hoc JĘZYKA SQL.

Używanie instrukcji SQL ad hoc

Rysunek 1. Używanie instrukcji AD-Hoc SQL

Na następnym ekranie możemy określić typ zapytania do utworzenia. Ponieważ to zapytanie zwróci pojedynczą wartość skalarną, łączna liczba rekordów w Products tabeli wybierz SELECT , która zwraca opcję wartości singe.

Konfigurowanie zapytania do używania instrukcji SELECT zwracającej pojedynczą wartość

Rysunek 2. Konfigurowanie zapytania do używania instrukcji SELECT zwracającej pojedynczą wartość

Po wskazaniu typu zapytania do użycia musimy następnie określić zapytanie.

Użyj zapytania SELECT COUNT(*) FROM Products

Rysunek 3. Korzystanie z zapytania SELECT COUNT(*) FROM Products

Na koniec określ nazwę metody . Jak wspomniano powyżej, użyjmy polecenia TotalNumberOfProducts.

Nazwij metodę DAL TotalNumberOfProducts

Rysunek 4. Nadaj metodzie DAL nazwę TotalNumberOfProducts

Po kliknięciu przycisku Zakończ kreator doda metodę TotalNumberOfProducts do dal. Metody zwracające skalarne w regionie DAL zwracają typy dopuszczające wartość null, jeśli wynik zapytania SQL to NULL. Nasze COUNT zapytanie zawsze zwraca jednak wartość innąNULL niż wartość, niezależnie od tego, metoda DAL zwraca liczbę całkowitą dopuszczaną do wartości null.

Oprócz metody DAL potrzebujemy również metody w usłudze BLL. ProductsBLL Otwórz plik klasy i dodaj metodę, która po prostu wywołuje metodę TotalNumberOfProducts DAL w dół do metody dalTotalNumberOfProducts:

public int TotalNumberOfProducts()
{
    return Adapter.TotalNumberOfProducts().GetValueOrDefault();
}

Metoda DAL TotalNumberOfProducts zwraca liczbę całkowitą dopuszczaną do wartości null, jednak utworzyliśmy metodę ProductsBLL klasy s TotalNumberOfProducts , aby zwracała standardową liczbę całkowitą. W związku z tym musimy mieć metodę ProductsBLL klasy s TotalNumberOfProducts zwracającą część wartości liczb całkowitych dopuszczających wartość null zwróconą przez metodę DAL.TotalNumberOfProducts Wywołanie metody GetValueOrDefault() zwraca wartość liczby całkowitej dopuszczanej do wartości null, jeśli istnieje. Jeśli liczba całkowita dopuszczana do wartości null to null, zwraca jednak domyślną wartość całkowitą 0.

Krok 3. Zwracanie dokładnego podzestawu rekordów

Następnym zadaniem jest utworzenie metod w dal i BLL, które akceptują zmienne Indeks wierszy początkowych i Maksymalna liczba wierszy omówionych wcześniej i zwracają odpowiednie rekordy. Zanim to zrobimy, najpierw przyjrzyjmy się potrzebnego skryptu SQL. Wyzwaniem, przed którym stoimy, jest to, że musimy mieć możliwość efektywnego przypisania indeksu do każdego wiersza w całych wynikach, dzięki czemu możemy zwrócić tylko te rekordy rozpoczynające się od indeksu wiersza początkowego (i maksymalną liczbę rekordów).

Nie jest to wyzwanie, jeśli w tabeli bazy danych znajduje się już kolumna, która służy jako indeks wiersza. Na pierwszy rzut oka możemy pomyśleć, że Products pole tabeli ProductID wystarczy, ponieważ pierwszy produkt ma ProductID wartość 1, drugi a 2 itd. Jednak usunięcie produktu pozostawia lukę w sekwencji, unieważniając to podejście.

Istnieją dwie ogólne techniki służące do wydajnego kojarzenia indeksu wierszy z danymi do strony, dzięki czemu można pobrać dokładny podzbiór rekordów:

  • Używając słowa kluczowego SQL Server 2005 nowego ROW_NUMBER() do SQL Server 2005 r., ROW_NUMBER() słowo kluczowe kojarzy klasyfikację z każdym zwróconym rekordem na podstawie pewnej kolejności. Tego rankingu można użyć jako indeksu wierszy dla każdego wiersza.

  • Używanie zmiennej tabeli i SET ROWCOUNT SQL Server instrukcjiSET ROWCOUNT można użyć do określenia liczby wszystkich rekordów, które ma przetwarzać zapytanie przed zakończeniem; zmienne tabeli to lokalne zmienne języka T-SQL, które mogą przechowywać dane tabelaryczne, na przykład tabele tymczasowe. Takie podejście działa równie dobrze zarówno w przypadku platformy Microsoft SQL Server 2005, jak i SQL Server 2000 (natomiast ROW_NUMBER() podejście działa tylko z SQL Server 2005 r.).

    Tutaj chodzi o utworzenie zmiennej tabeli, która zawiera kolumnę IDENTITY i kolumny dla kluczy podstawowych tabeli, której dane są stronicowane. Następnie zawartość tabeli, której dane są stronicowane, jest po cenach dumpingowych do zmiennej tabeli, co powoduje skojarzenie sekwencyjnego indeksu wierszy (za pośrednictwem IDENTITY kolumny) dla każdego rekordu w tabeli. Po wypełnieniu zmiennej tabeli można wykonać instrukcję w zmiennej tabeli tabeli połączonej z tabelą bazową, aby ściągnąć SELECT określone rekordy. Instrukcja SET ROWCOUNT jest używana do inteligentnego ograniczania liczby rekordów, które muszą być po cenach dumpingowych do zmiennej tabeli.

    Ta wydajność podejścia jest oparta na żądanym numerze strony, ponieważ SET ROWCOUNT wartość jest przypisywana do indeksu wierszy początkowych oraz maksymalnej liczby wierszy. W przypadku stronicowania za pośrednictwem stron z małą liczbą, takich jak kilka pierwszych stron danych, takie podejście jest bardzo wydajne. Jednak podczas pobierania strony w pobliżu końca ma ona domyślną wydajność podobną do stronicowania.

Ten samouczek implementuje niestandardowe stronicowanie przy użyciu słowa kluczowego ROW_NUMBER() . Aby uzyskać więcej informacji na temat używania zmiennej i SET ROWCOUNT techniki tabeli, zobacz Efektywne stronicowanie za pośrednictwem dużych ilości danych.

Słowo ROW_NUMBER() kluczowe skojarzyło klasyfikację z każdym rekordem zwróconym w określonej kolejności przy użyciu następującej składni:

SELECT columnList,
       ROW_NUMBER() OVER(orderByClause)
FROM TableName

ROW_NUMBER() Zwraca wartość liczbową, która określa rangę dla każdego rekordu w odniesieniu do wskazanej kolejności. Aby na przykład wyświetlić rangę dla każdego produktu uporządkowanego od najdroższego do najmniejszego, możemy użyć następującego zapytania:

SELECT ProductName, UnitPrice,
       ROW_NUMBER() OVER(ORDER BY UnitPrice DESC) AS PriceRank
FROM Products

Rysunek 5 przedstawia wyniki tego zapytania po uruchomieniu okna zapytania w programie Visual Studio. Należy pamiętać, że produkty są uporządkowane według ceny wraz z klasyfikacją cen dla każdego wiersza.

Pozycja ceny jest uwzględniona dla każdego zwróconego rekordu

Rysunek 5. Pozycja ceny jest uwzględniana dla każdego zwróconego rekordu

Uwaga

ROW_NUMBER()jest tylko jedną z wielu nowych funkcji klasyfikacji dostępnych w SQL Server 2005 roku. Aby zapoznać się z bardziej szczegółowym omówieniem funkcji ROW_NUMBER(), wraz z innymi funkcjami klasyfikacji, przeczytaj Artykuł Returning Ranked Results with Microsoft SQL Server 2005 (Zwracanie wyników klasyfikacji w firmie Microsoft SQL Server 2005).

Podczas klasyfikowania wyników według określonej ORDER BY kolumny w OVER klauzuli (UnitPricew powyższym przykładzie) SQL Server musi posortować wyniki. Jest to szybka operacja, jeśli istnieje indeks grupowany dla kolumn, według których wyniki są uporządkowane lub jeśli istnieje indeks obejmujący, ale w przeciwnym razie może być bardziej kosztowne. Aby zwiększyć wydajność dla wystarczająco dużych zapytań, rozważ dodanie indeksu nieklasowanego dla kolumny, według której wyniki są uporządkowane według. Zobacz Funkcje klasyfikacji i wydajność w SQL Server 2005, aby uzyskać bardziej szczegółowe informacje na temat zagadnień dotyczących wydajności.

Informacje o klasyfikacji zwracane przez program ROW_NUMBER() nie mogą być bezpośrednio używane w klauzuli WHERE . Jednak do zwrócenia wyniku można użyć tabeli pochodnej ROW_NUMBER() , która może pojawić się w klauzuli WHERE . Na przykład następujące zapytanie używa tabeli pochodnej, aby zwrócić kolumny ProductName i UnitPrice wraz z ROW_NUMBER() wynikiem, a następnie używa WHERE klauzuli , aby zwrócić tylko te produkty, których ranga cen wynosi od 11 do 20:

SELECT PriceRank, ProductName, UnitPrice
FROM
   (SELECT ProductName, UnitPrice,
       ROW_NUMBER() OVER(ORDER BY UnitPrice DESC) AS PriceRank
    FROM Products
   ) AS ProductsWithRowNumber
WHERE PriceRank BETWEEN 11 AND 20

Rozszerzenie tej koncepcji nieco dalej. Możemy użyć tej metody, aby pobrać określoną stronę danych, biorąc pod uwagę żądane wartości Indeks wiersza początkowego i Maksymalna liczba wierszy:

SELECT PriceRank, ProductName, UnitPrice
FROM
   (SELECT ProductName, UnitPrice,
       ROW_NUMBER() OVER(ORDER BY UnitPrice DESC) AS PriceRank
    FROM Products
   ) AS ProductsWithRowNumber
WHERE PriceRank > <i>StartRowIndex</i> AND
    PriceRank <= (<i>StartRowIndex</i> + <i>MaximumRows</i>)

Uwaga

Jak zobaczymy w dalszej części tego samouczka, StartRowIndex indeksowany przez element ObjectDataSource jest indeksowany od zera, natomiast ROW_NUMBER() wartość zwrócona przez SQL Server 2005 jest indeksowana od 1. W związku z tym klauzula WHERE zwraca te rekordy, w których PriceRank jest ściśle większa niż i mniejsza niż StartRowIndex lub równa StartRowIndex + MaximumRows.

Teraz, gdy już omówiliśmy, jak ROW_NUMBER() można użyć do pobrania określonej strony danych przy użyciu wartości Indeks początkowy wierszy i Maksymalna liczba wierszy, teraz musimy zaimplementować tę logikę jako metody w dal i BLL.

Podczas tworzenia tego zapytania musimy zdecydować o kolejności, według której będą klasyfikowane wyniki; posortujmy produkty według ich nazwy w kolejności alfabetycznej. Oznacza to, że w przypadku niestandardowej implementacji stronicowania w tym samouczku nie będziemy mogli utworzyć niestandardowego raportu stronicowanego, niż można również sortować. Jednak w następnym samouczku zobaczymy, jak można udostępnić takie funkcje.

W poprzedniej sekcji utworzyliśmy metodę DAL jako instrukcję ad hoc języka SQL. Niestety analizator języka T-SQL w programie Visual Studio używany przez kreatora TableAdapter nie lubi OVER składni używanej ROW_NUMBER() przez funkcję. Dlatego musimy utworzyć tę metodę DAL jako procedurę składowaną. Wybierz Eksploratora serwera z menu Widok (lub naciśnij klawisze Ctrl+Alt+S) i rozwiń NORTHWND.MDF węzeł. Aby dodać nową procedurę składowaną, kliknij prawym przyciskiem myszy węzeł Procedury składowane i wybierz polecenie Dodaj nową procedurę składowaną (zobacz Rysunek 6).

Dodawanie nowej procedury składowanej na potrzeby stronicowania za pośrednictwem produktów

Rysunek 6. Dodawanie nowej procedury składowanej na potrzeby stronicowania za pośrednictwem produktów

Ta procedura składowana powinna akceptować dwa parametry wejściowe liczby całkowitej — @startRowIndex i używać ROW_NUMBER() funkcji uporządkowanej ProductName przez pole, zwracając tylko te wiersze większe niż określone @startRowIndex i mniejsze niż lub równe @startRowIndex + @maximumRow s.@maximumRows Wprowadź następujący skrypt w nowej procedurze składowanej, a następnie kliknij ikonę Zapisz, aby dodać procedurę składowaną do bazy danych.

CREATE PROCEDURE dbo.GetProductsPaged
(
    @startRowIndex int,
    @maximumRows int
)
AS
    SELECT     ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
               UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
               CategoryName, SupplierName
FROM
   (
       SELECT ProductID, ProductName, SupplierID, CategoryID, QuantityPerUnit,
              UnitPrice, UnitsInStock, UnitsOnOrder, ReorderLevel, Discontinued,
              (SELECT CategoryName
               FROM Categories
               WHERE Categories.CategoryID = Products.CategoryID) AS CategoryName,
              (SELECT CompanyName
               FROM Suppliers
               WHERE Suppliers.SupplierID = Products.SupplierID) AS SupplierName,
              ROW_NUMBER() OVER (ORDER BY ProductName) AS RowRank
        FROM Products
    ) AS ProductsWithRowNumbers
WHERE RowRank > @startRowIndex AND RowRank <= (@startRowIndex + @maximumRows)

Po utworzeniu procedury składowanej poświęć chwilę na jej przetestowanie. Kliknij prawym przyciskiem myszy GetProductsPaged nazwę procedury składowanej w Eksploratorze serwera i wybierz opcję Wykonaj. Program Visual Studio wyświetli monit o podanie parametrów @startRowIndex wejściowych i @maximumRow s (zobacz Rysunek 7). Wypróbuj różne wartości i sprawdź wyniki.

Wprowadź wartość dla <span class=@startRowIndex and @maximumRows Parameters" />

Rysunek 7. Wprowadź wartość parametrów @startRowIndex i @maximumRows

Po wybraniu tych wartości parametrów wejściowych w oknie Dane wyjściowe zostaną wyświetlone wyniki. Rysunek 8 przedstawia wyniki podczas przekazywania wartości 10 dla parametrów @startRowIndex i @maximumRows .

Zwracane są rekordy, które pojawią się na drugiej stronie danych

Rysunek 8. Zwracane są rekordy wyświetlane na drugiej stronie danych (kliknij, aby wyświetlić obraz w pełnym rozmiarze)

Po utworzeniu tej procedury składowanej możemy utworzyć metodę ProductsTableAdapter . Otwórz typowy zestaw Northwind.xsd danych, kliknij prawym przyciskiem myszy ProductsTableAdapterelement , a następnie wybierz opcję Dodaj zapytanie. Zamiast tworzyć zapytanie przy użyciu instrukcji ad hoc SQL, utwórz je przy użyciu istniejącej procedury składowanej.

Tworzenie metody DAL przy użyciu istniejącej procedury składowanej

Rysunek 9. Tworzenie metody DAL przy użyciu istniejącej procedury składowanej

Następnie zostanie wyświetlony monit o wybranie procedury składowanej do wywołania. Wybierz procedurę GetProductsPaged składowaną z listy rozwijanej.

Wybierz procedurę składowaną GetProductsPaged z listy Drop-Down

Rysunek 10. Wybieranie procedury składowanej GetProductsPaged z listy Drop-Down

Następny ekran wyświetla następnie pytanie, jakiego rodzaju dane są zwracane przez procedurę składowaną: dane tabelaryczne, pojedynczą wartość lub brak wartości. GetProductsPaged Ponieważ procedura składowana może zwrócić wiele rekordów, wskaż, że zwraca dane tabelaryczne.

Wskazuje, że procedura składowana zwraca dane tabelaryczne

Rysunek 11. Wskazuje, że procedura składowana zwraca dane tabelaryczne

Na koniec wskaż nazwy metod, które chcesz utworzyć. Podobnie jak w przypadku naszych poprzednich samouczków, utwórz metody przy użyciu tabel Fill a DataTable i Return a DataTable. Nadaj pierwszej metodzie FillPaged nazwę i drugą GetProductsPagedmetodę .

Nazwij metody FillPaged i GetProductsPaged

Rysunek 12. Nadaj metodom nazwę FillPaged i GetProductsPaged

Oprócz utworzenia metody DAL, aby zwrócić określoną stronę produktów, musimy również zapewnić takie funkcje w usłudze BLL. Podobnie jak w przypadku metody DAL, metoda GetProductsPaged BLL musi akceptować dwa dane wejściowe liczb całkowitych w celu określenia indeksu wiersza początkowego i maksymalnej liczby wierszy i musi zwrócić tylko te rekordy, które mieszczą się w określonym zakresie. Utwórz taką metodę BLL w klasie ProductsBLL, która jedynie wywołuje metodę GetProductsPaged w dół, w następujący sposób:

[System.ComponentModel.DataObjectMethodAttribute(
    System.ComponentModel.DataObjectMethodType.Select, false)]
public Northwind.ProductsDataTable GetProductsPaged(int startRowIndex, int maximumRows)
{
    return Adapter.GetProductsPaged(startRowIndex, maximumRows);
}

Możesz użyć dowolnej nazwy parametrów wejściowych metody BLL, ale jak wkrótce zobaczymy, decydujemy się na użycie startRowIndex i maximumRows zapisuje nas z dodatkowego fragmentu pracy podczas konfigurowania obiektu ObjectDataSource do użycia tej metody.

Krok 4. Konfigurowanie obiektu ObjectDataSource do używania niestandardowego stronicowania

Po zakończeniu uzyskiwania dostępu do określonego podzestawu rekordów za pomocą metod BLL i DAL można utworzyć kontrolkę GridView, która udostępnia strony za pośrednictwem jego rekordów bazowych przy użyciu niestandardowego stronicowania. Zacznij od otwarcia EfficientPaging.aspx strony w folderze PagingAndSorting , dodania kontrolki GridView do strony i skonfigurowania jej do używania nowej kontrolki ObjectDataSource. W naszych poprzednich samouczkach często używaliśmy metody s GetProducts klasy ObjectDataSource.ProductsBLL Tym razem jednak chcemy użyć GetProductsPaged metody , ponieważ GetProducts metoda zwraca wszystkie produkty w bazie danych, natomiast GetProductsPaged zwraca tylko określony podzestaw rekordów.

Konfigurowanie obiektu ObjectDataSource do używania metody GetProductsPaged klasy ProductsBLL

Rysunek 13. Konfigurowanie obiektu ObjectDataSource do używania metody GetProductsPaged klasy ProductsBLL

Ponieważ tworzysz obiekt GridView tylko do odczytu, poświęć chwilę, aby ustawić listę rozwijaną metody na kartach INSERT, UPDATE i DELETE na wartość (Brak).

Następnie kreator ObjectDataSource wyświetli monit o podanie źródeł wartości parametrów wejściowych GetProductsPaged i maximumRows metodstartRowIndex. Te parametry wejściowe zostaną ustawione automatycznie przez kontrolkę GridView, więc po prostu pozostaw źródło ustawione na Wartość Brak i kliknij przycisk Zakończ.

Pozostaw źródła parametrów wejściowych jako Brak

Rysunek 14. Pozostaw źródła parametrów wejściowych jako Brak

Po zakończeniu pracy kreatora ObjectDataSource kontrolka GridView będzie zawierać pole BoundField lub CheckBoxField dla każdego pola danych produktu. Możesz dostosować wygląd GridView zgodnie z potrzebami. Zdecydowałem się wyświetlić tylko ProductName, , CategoryNameSupplierName, QuantityPerUniti UnitPrice BoundFields. Ponadto skonfiguruj obiekt GridView do obsługi stronicowania, zaznaczając pole wyboru Włącz stronicowanie w swoim tagu inteligentnym. Po tych zmianach znaczniki deklaratywne GridView i ObjectDataSource powinny wyglądać podobnie do następujących:

<asp:GridView ID="GridView1" runat="server" AutoGenerateColumns="False"
    DataKeyNames="ProductID" DataSourceID="ObjectDataSource1" AllowPaging="True">
    <Columns>
        <asp:BoundField DataField="ProductName" HeaderText="Product"
            SortExpression="ProductName" />
        <asp:BoundField DataField="CategoryName" HeaderText="Category"
            ReadOnly="True" SortExpression="CategoryName" />
        <asp:BoundField DataField="SupplierName" HeaderText="Supplier"
            SortExpression="SupplierName" />
        <asp:BoundField DataField="QuantityPerUnit" HeaderText="Qty/Unit"
            SortExpression="QuantityPerUnit" />
        <asp:BoundField DataField="UnitPrice" DataFormatString="{0:c}"
            HeaderText="Price" HtmlEncode="False" SortExpression="UnitPrice" />
    </Columns>
</asp:GridView>
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
    OldValuesParameterFormatString="original_{0}" SelectMethod="GetProductsPaged"
    TypeName="ProductsBLL">
    <SelectParameters>
        <asp:Parameter Name="startRowIndex" Type="Int32" />
        <asp:Parameter Name="maximumRows" Type="Int32" />
    </SelectParameters>
</asp:ObjectDataSource>

Jeśli jednak odwiedzasz stronę za pośrednictwem przeglądarki, element GridView nie znajduje się tam, gdzie można go znaleźć.

Kontrolka GridView nie jest wyświetlana

Rysunek 15. Kontrolka GridView nie jest wyświetlana

Brak obiektu GridView, ponieważ element ObjectDataSource obecnie używa wartości 0 jako wartości parametrów wejściowych GetProductsPagedstartRowIndex i .maximumRows W związku z tym wynikowe zapytanie SQL nie zwraca żadnych rekordów i dlatego obiekt GridView nie jest wyświetlany.

Aby rozwiązać ten problem, musimy skonfigurować obiekt ObjectDataSource do korzystania z niestandardowego stronicowania. Można to zrobić w następujących krokach:

  1. Ustaw właściwość ObjectDataSource EnablePaging na true wartość wskazującą obiekt ObjectDataSource, który musi zostać przekazany do SelectMethod dwóch dodatkowych parametrów: jeden, aby określić indeks wiersza początkowego () i jeden, aby określić maksymalną liczbę wierszy (StartRowIndexParameterNameMaximumRowsParameterName).
  2. Ustaw właściwości i MaximumRowsParameterName ObjectDataSource StartRowIndexParameterName odpowiednioStartRowIndexParameterName właściwości i MaximumRowsParameterName wskazują nazwy parametrów wejściowych przekazanych do SelectMethod obiektu na potrzeby stronicowania niestandardowego. Domyślnie te nazwy parametrów to startIndexRow i maximumRows, dlatego podczas tworzenia GetProductsPaged metody w bibliotece BLL użyto tych wartości dla parametrów wejściowych. Jeśli chcesz użyć różnych nazw parametrów dla metody BLL, GetProductsPaged takiej jak startIndex i maxRows, na przykład należy odpowiednio ustawić właściwości ObjectDataSource i StartRowIndexParameterNameMaximumRowsParameterName (na przykład startIndex dla StartRowIndexParameterName i maxRows dla MaximumRowsParameterName).
  3. Ustaw właściwość ObjectDataSource SelectCountMethod na nazwę metody, która zwraca łączną liczbę rekordów stronicowanych przez (TotalNumberOfProducts), przypomnij sobie, że ProductsBLL metoda klasy TotalNumberOfProducts zwraca łączną liczbę rekordów s, używając metody DAL, która wykonuje SELECT COUNT(*) FROM Products zapytanie. Te informacje są wymagane przez obiekt ObjectDataSource w celu poprawnego renderowania interfejsu stronicowania.
  4. startRowIndex Usuń elementy i maximumRows<asp:Parameter> z znaczników deklaratywnego objectDataSource podczas konfigurowania obiektu ObjectDataSource za pośrednictwem kreatora, program Visual Studio automatycznie dodał dwa <asp:Parameter> elementy dla GetProductsPaged parametrów wejściowych metody. Po ustawieniu wartości EnablePagingtrueparametrów te zostaną przekazane automatycznie. Jeśli pojawią się one również w składni deklaratywnej, obiekt ObjectDataSource podejmie próbę przekazania czterech parametrów do GetProductsPaged metody i dwóch parametrów do TotalNumberOfProducts metody. Jeśli zapomnisz usunąć te <asp:Parameter> elementy, podczas odwiedzania strony za pośrednictwem przeglądarki zostanie wyświetlony komunikat o błędzie, taki jak : ObjectDataSource 'ObjectDataSource1' nie może odnaleźć metody innej niż ogólna "TotalNumberOfProducts", która zawiera parametry: startRowIndex, maximumRows.

Po wprowadzeniu tych zmian składnia deklaratywna obiektu ObjectDataSource powinna wyglądać następująco:

<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
    OldValuesParameterFormatString="original_{0}" TypeName="ProductsBLL"
    SelectMethod="GetProductsPaged" EnablePaging="True"
    SelectCountMethod="TotalNumberOfProducts">
</asp:ObjectDataSource>

Należy pamiętać, że EnablePaging właściwości i SelectCountMethod zostały ustawione, a <asp:Parameter> elementy zostały usunięte. Rysunek 16 przedstawia zrzut ekranu okno Właściwości po wprowadzeniu tych zmian.

Aby użyć niestandardowego stronicowania, skonfiguruj kontrolkę ObjectDataSource

Rysunek 16. Aby użyć niestandardowego stronicowania, skonfiguruj kontrolkę ObjectDataSource

Po wprowadzeniu tych zmian odwiedź tę stronę za pośrednictwem przeglądarki. Powinna zostać wyświetlona lista 10 produktów uporządkowanych alfabetycznie. Poświęć chwilę, aby przejść przez dane po jednej stronie naraz. Chociaż nie ma żadnej różnicy wizualnej w perspektywie użytkownika końcowego między domyślnym stronicowaniem i niestandardowym stronicowaniem, niestandardowe stronicowanie bardziej wydajne strony za pośrednictwem dużych ilości danych, ponieważ pobiera tylko te rekordy, które muszą być wyświetlane dla danej strony.

Dane uporządkowane według nazwy produktu są stronicowane przy użyciu niestandardowego stronicowania

Rysunek 17. Dane uporządkowane według nazwy produktu są stronicowane przy użyciu niestandardowego stronicowania (kliknij, aby wyświetlić obraz o pełnym rozmiarze)

Uwaga

W przypadku niestandardowego stronicowania wartość liczby stron zwrócona SelectCountMethod przez element ObjectDataSource jest przechowywana w stanie widoku gridView. Inne zmienne PageIndexGridView , EditIndex, SelectedIndexDataKeys kolekcja itd. są przechowywane w stanie kontroli, który jest utrwalany niezależnie od wartości właściwości GridViewEnableViewState. PageCount Ponieważ wartość jest utrwalana w ogłaszaniu zwrotnym przy użyciu stanu widoku, w przypadku korzystania z interfejsu stronicowania, który zawiera link umożliwiający przejście do ostatniej strony, konieczne jest włączenie stanu widoku kontrolki GridView. (Jeśli interfejs stronicowania nie zawiera bezpośredniego linku do ostatniej strony, możesz wyłączyć stan widoku).

Kliknięcie linku ostatniej strony powoduje ogłaszanie zwrotne i powoduje, że obiekt GridView zaktualizuje jego PageIndex właściwość. Jeśli ostatni link do strony zostanie kliknięty, kontrolka GridView przypisze jej PageIndex właściwość do wartości mniejszą niż jej PageCount właściwość. Po wyłączeniu PageCount stanu widoku wartość jest utracona w przypadku ogłaszania zwrotnego, a PageIndex zamiast tego jest przypisana maksymalna wartość całkowita. Następnie kontrolka GridView próbuje określić indeks wiersza początkowego przez pomnożenie PageSize właściwości i PageCount . Powoduje to, OverflowException że produkt przekracza maksymalny dozwolony rozmiar liczby całkowitej.

Implementowanie niestandardowego stronicowania i sortowania

Nasza bieżąca niestandardowa implementacja stronicowania wymaga określenia kolejności, za pomocą której dane są stronicowane statycznie podczas tworzenia GetProductsPaged procedury składowanej. Można jednak zauważyć, że tag inteligentny GridView zawiera pole wyboru Włącz sortowanie oprócz opcji Włącz stronicowanie. Niestety dodanie obsługi sortowania do kontrolki GridView przy użyciu bieżącej niestandardowej implementacji stronicowania spowoduje posortowanie rekordów tylko na aktualnie wyświetlanej stronie danych. Jeśli na przykład skonfigurujesz kontrolkę GridView tak, aby obsługiwała stronicowanie, a następnie podczas wyświetlania pierwszej strony danych sortuj według nazwy produktu w kolejności malejącej, spowoduje to odwrócenie kolejności produktów na stronie 1. Jak pokazano na rysunku 18, takie jak Carnarvon Tigers jako pierwszy produkt podczas sortowania w odwrotnej kolejności alfabetycznej, który ignoruje 71 innych produktów, które pochodzą po Carnarvon Tigers, alfabetycznie; tylko te rekordy na pierwszej stronie są brane pod uwagę podczas sortowania.

Sortowane są tylko dane wyświetlane na bieżącej stronie

Rysunek 18. Posortowane są tylko dane wyświetlane na bieżącej stronie (kliknij, aby wyświetlić obraz pełnowymiarowy)

Sortowanie dotyczy tylko bieżącej strony danych, ponieważ sortowanie odbywa się po pobraniu danych z metody BLL GetProductsPaged , a ta metoda zwraca tylko te rekordy dla określonej strony. Aby poprawnie zaimplementować sortowanie, musimy przekazać wyrażenie sortowania do GetProductsPaged metody , aby dane można było odpowiednio sklasyfikować przed zwróceniem określonej strony danych. Zobaczymy, jak to zrobić w następnym samouczku.

Implementowanie niestandardowego stronicowania i usuwanie

W przypadku włączenia usuwania funkcji w siatceView, której dane są stronicowane przy użyciu niestandardowych technik stronicowania, przekonasz się, że podczas usuwania ostatniego rekordu z ostatniej strony widok GridView zniknie, a nie odpowiednio zdekrementuje element GridView s PageIndex. Aby odtworzyć tę usterkę, włącz usuwanie właśnie utworzonego samouczka. Przejdź do ostatniej strony (strona 9), gdzie powinien zostać wyświetlony pojedynczy produkt, ponieważ stronicujemy do 81 produktów, 10 produktów jednocześnie. Usuń ten produkt.

Po usunięciu ostatniego produktu kontrolka GridView powinna automatycznie przejść do ósmej strony, a takie funkcje są wyświetlane z domyślnym stronicowaniem. Jednak po usunięciu ostatniego produktu na ostatniej stronie funkcja GridView po prostu znika z ekranu po usunięciu niestandardowego elementu. Dokładny powód, dla którego tak się dzieje, wykracza nieco poza zakres tego samouczka; Aby uzyskać informacje o źródle tego problemu, zobacz Usuwanie ostatniego rekordu na ostatniej stronie z kontrolki GridView z niestandardowym stronicowaniem . Podsumowując, wynika to z następującej sekwencji kroków wykonywanych przez obiekt GridView po kliknięciu przycisku Usuń:

  1. Usuwanie rekordu
  2. Pobierz odpowiednie rekordy do wyświetlenia dla określonego PageIndex elementu i PageSize
  3. Upewnij się, że PageIndex obiekt nie przekracza liczby stron danych w źródle danych; jeśli tak, automatycznie dekrementuje właściwość GridView PageIndex
  4. Wiązanie odpowiedniej strony danych z kontrolką GridView przy użyciu rekordów uzyskanych w kroku 2

Problem wynika z faktu, że w kroku 2 PageIndex używany podczas chwytania rekordów do wyświetlenia jest nadal PageIndex z ostatniej strony, której jedyny rekord został właśnie usunięty. W związku z tym w kroku 2 żadne rekordy nie są zwracane od ostatniej strony danych, które nie zawierają już żadnych rekordów. Następnie, w kroku 3, GridView zdaje sobie sprawę, że jego PageIndex właściwość jest większa niż całkowita liczba stron w źródle danych (ponieważ usunęliśmy ostatni rekord na ostatniej stronie), a zatem dekrementuje jego PageIndex właściwość. W kroku 4 kontrolka GridView próbuje powiązać się z danymi pobranymi w kroku 2; jednak w kroku 2 nie zostały zwrócone żadne rekordy, co powoduje, że element GridView jest pusty. W przypadku domyślnego stronicowania ten problem nie występuje, ponieważ w kroku 2 wszystkie rekordy są pobierane ze źródła danych.

Aby rozwiązać ten problem, mamy dwie opcje. Pierwszym z nich jest utworzenie procedury obsługi zdarzeń dla programu obsługi zdarzeń GridView RowDeleted , który określa, ile rekordów było wyświetlanych na stronie, która została właśnie usunięta. Jeśli był tylko jeden rekord, to właśnie usunięty rekord musiał być ostatnim i musimy usunąć obiekt GridView s PageIndex. Oczywiście chcemy zaktualizować PageIndex właściwość tylko wtedy, gdy operacja usuwania zakończyła się pomyślnie, co można określić, upewniając się, że e.Exception właściwość ma wartość null.

To podejście działa, ponieważ aktualizuje element PageIndex po kroku 1, ale przed krokiem 2. W związku z tym w kroku 2 zwracany jest odpowiedni zestaw rekordów. W tym celu użyj kodu podobnego do następującego:

protected void GridView1_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
    // If we just deleted the last row in the GridView, decrement the PageIndex
    if (e.Exception == null && GridView1.Rows.Count == 1)
        // we just deleted the last row
        GridView1.PageIndex = Math.Max(0, GridView1.PageIndex - 1);
}

Alternatywnym obejściem jest utworzenie procedury obsługi zdarzeń dla zdarzenia ObjectDataSource RowDeleted i ustawienie AffectedRows właściwości na wartość 1. Po usunięciu rekordu w kroku 1 (ale przed ponownym pobraniem danych w kroku 2) kontrolka GridView aktualizuje jego PageIndex właściwość, jeśli operacja ma wpływ na co najmniej jeden wiersz. Jednak AffectedRows właściwość nie jest ustawiana przez obiekt ObjectDataSource i dlatego ten krok zostanie pominięty. Jednym ze sposobów wykonania tego kroku jest ręczne ustawienie AffectedRows właściwości, jeśli operacja usuwania zakończy się pomyślnie. Można to zrobić przy użyciu kodu w następujący sposób:

protected void ObjectDataSource1_Deleted(
    object sender, ObjectDataSourceStatusEventArgs e)
{
    // If we get back a Boolean value from the DeleteProduct method and it's true,
    // then we successfully deleted the product. Set AffectedRows to 1
    if (e.ReturnValue is bool && ((bool)e.ReturnValue) == true)
        e.AffectedRows = 1;
}

Kod dla obu tych procedur obsługi zdarzeń można znaleźć w klasie kodu w przykładzie EfficientPaging.aspx .

Porównywanie wydajności stronicowania domyślnego i niestandardowego

Ponieważ niestandardowe stronicowanie pobiera tylko wymagane rekordy, natomiast domyślne stronicowanie zwraca wszystkie rekordy dla każdej wyświetlanej strony, jest jasne, że stronicowanie niestandardowe jest bardziej wydajne niż domyślne stronicowanie. Ale o ile bardziej wydajne jest stronicowanie niestandardowe? Jakie korzyści z wydajności można zobaczyć, przechodząc z domyślnego stronicowania do niestandardowego stronicowania?

Niestety, nie ma jednego rozmiaru pasuje do wszystkich odpowiedzi tutaj. Wzrost wydajności zależy od wielu czynników, a najwybitniejszym dwoma rekordami jest liczba stronicowanych rekordów, a obciążenie umieszczone na serwerze bazy danych i kanałach komunikacyjnych między serwerem internetowym a serwerem bazy danych. W przypadku małych tabel z zaledwie kilkoma tuzinami rekordów różnica wydajności może być nieznaczna. W przypadku dużych tabel, z tysiącami do setek tysięcy wierszy, różnica wydajności jest jednak ostra.

Artykuł "Niestandardowe stronicowanie w ASP.NET 2.0 z SQL Server 2005", zawiera kilka testów wydajności, które przeprowadziłem, aby wykazać różnice w wydajności między tymi dwoma technikami stronicowania podczas stronicowania za pomocą tabeli bazy danych z 50 000 rekordów. W tych testach zbadałem zarówno czas wykonywania zapytania na poziomie SQL Server (przy użyciu programu SQL Profiler), jak i na stronie ASP.NET przy użyciu funkcji śledzenia ASP.NET. Należy pamiętać, że te testy zostały uruchomione w moim polu programistycznym z jednym aktywnym użytkownikiem, a zatem są nieumyślne i nie naśladują typowych wzorców obciążenia witryn internetowych. Niezależnie od tego wyniki ilustrują względne różnice w czasie wykonywania dla domyślnego i niestandardowego stronicowania podczas pracy z wystarczająco dużą ilością danych.

Średni czas trwania (s) Odczytuje
Domyślny profiler SQL stronicowania 1.411 383
Niestandardowy profiler SQL stronicowania 0.002 29
Domyślne śledzenie ASP.NET stronicowania 2.379 Nie dotyczy
Niestandardowe śledzenie ASP.NET stronicowania 0.029 Nie dotyczy

Jak widać, pobieranie określonej strony danych wymaga średnio 354 mniej operacji odczytu i ukończenia w ułamku czasu. Na stronie ASP.NET niestandardowa strona była w stanie renderować w pobliżu 1/100 czasu , jaki upłynął podczas korzystania z domyślnego stronicowania.

Podsumowanie

Domyślne stronicowanie to cinch, który umożliwia zaimplementowanie tylko zaznaczenia pola wyboru Włącz stronicowanie w tagu inteligentnym kontrolki sieci Web danych, ale taka prostota jest kosztem wydajności. W przypadku domyślnego stronicowania, gdy użytkownik zażąda dowolnej strony danych , są zwracane wszystkie rekordy, mimo że może być wyświetlana tylko niewielka część z nich. W celu zwalczania tego obciążenia związanego z wydajnością usługa ObjectDataSource oferuje alternatywną opcję stronicowania niestandardowego.

Podczas gdy niestandardowe stronicowanie poprawia się po domyślnych problemach z wydajnością stronicowania przez pobieranie tylko tych rekordów, które muszą być wyświetlane, jest bardziej zaangażowane w implementację niestandardowego stronicowania. Najpierw należy napisać zapytanie, które poprawnie (i skutecznie) uzyskuje dostęp do określonego podzestawu żądanych rekordów. Można to osiągnąć na wiele sposobów; ta, która została zbadana w tym samouczku, to użycie nowej ROW_NUMBER() funkcji SQL Server 2005 w celu sklasyfikowania wyników, a następnie zwrócenia tylko tych wyników, których klasyfikacja mieści się w określonym zakresie. Ponadto musimy dodać metodę określania całkowitej liczby rekordów, za pomocą których są stronicowane. Po utworzeniu tych metod DAL i BLL należy również skonfigurować obiekt ObjectDataSource, aby określić, ile rekordów sum jest stronicowanych, i może poprawnie przekazać wartości Indeks wiersza początkowego i Maksymalna liczba wierszy do biblioteki BLL.

Chociaż implementowanie niestandardowego stronicowania wymaga wielu kroków i nie jest tak proste, jak domyślne stronicowanie, stronicowanie niestandardowe jest koniecznością podczas stronicowania wystarczająco dużych ilości danych. Jak pokazano w badaniu wyników, stronicowanie niestandardowe może rzucić sekundy od czasu renderowania strony ASP.NET i może rozjaśnić obciążenie na serwerze bazy danych o jeden lub więcej rzędów wielkości.

Szczęśliwe programowanie!

Informacje o autorze

Scott Mitchell, autor siedmiu książek ASP/ASP.NET i założyciel 4GuysFromRolla.com, współpracuje z technologiami internetowymi firmy Microsoft od 1998 roku. Scott pracuje jako niezależny konsultant, trener i pisarz. Jego najnowsza książka to Sams Teach Yourself ASP.NET 2.0 w ciągu 24 godzin. Można do niego dotrzeć pod adresem mitchell@4GuysFromRolla.com. Lub za pośrednictwem swojego bloga, który można znaleźć na stronie http://ScottOnWriting.NET.