Účinné stránkování velkých objemů dat (C#)

Scott Mitchell

Stáhnout PDF

Výchozí možnost stránkování ovládacího prvku prezentace dat není vhodná při práci s velkými objemy dat, protože jeho podkladový ovládací prvek zdroje dat načítá všechny záznamy, i když se zobrazí jenom podmnožina dat. Za takových okolností se musíme obrátit na vlastní stránkování.

Úvod

Jak jsme probrali v předchozím kurzu, stránkování je možné implementovat jedním ze dvou způsobů:

  • Výchozí stránkování lze implementovat jednoduše zaškrtnutím možnosti Povolit stránkování v inteligentní značce webového ovládacího prvku dat; Při každém zobrazení stránky dat však ObjectDataSource načte všechny záznamy, i když se na stránce zobrazí pouze jejich podmnožina.
  • Vlastní stránkování zlepšuje výkon výchozího stránkování tím, že načítá pouze ty záznamy z databáze, které je třeba zobrazit pro konkrétní stránku dat požadovaných uživatelem; Vlastní stránkování však vyžaduje trochu větší úsilí než výchozí stránkování.

Vzhledem k jednoduchosti implementace stačí zaškrtnout políčko a máte hotovo! výchozí stránkování je atraktivní možnost. Jeho naivní přístup při načítání všech záznamů však z něj činí nepředpočitatelnou volbu při stránkování dostatečně velkých objemů dat nebo pro weby s mnoha souběžnými uživateli. Za takových okolností se musíme obrátit na vlastní stránkování, abychom zajistili responzivní systém.

Úkolem vlastního stránkování je napsat dotaz, který vrátí přesnou sadu záznamů potřebných pro konkrétní stránku dat. Microsoft SQL Server 2005 naštěstí poskytuje nové klíčové slovo pro výsledky řazení, které nám umožňuje napsat dotaz, který dokáže efektivně načíst správnou podmnožinu záznamů. V tomto kurzu se dozvíte, jak použít toto nové klíčové slovo SQL Server 2005 k implementaci vlastního stránkování v ovládacím prvku GridView. I když je uživatelské rozhraní pro vlastní stránkování stejné jako u výchozího stránkování, krokování z jedné stránky na druhou pomocí vlastního stránkování může být o několik řádů rychlejší než výchozí stránkování.

Poznámka

Přesný nárůst výkonu, který vlastní stránkování vykazuje, závisí na celkovém počtu stránkovaných záznamů a zatížení na databázovém serveru. Na konci tohoto kurzu se podíváme na některé hrubé metriky, které ukazují výhody výkonu získaného vlastním stránkováním.

Krok 1: Principy vlastního procesu stránkování

Při stránkování dat závisí přesné záznamy zobrazené na stránce požadovaných dat a počtu zobrazených záznamů na stránce. Představte si například, že chceme stránkovat 81 produktů a zobrazit 10 produktů na stránku. Při prohlížení první stránky chceme produkty 1 až 10; při prohlížení druhé stránky by nás zajímaly produkty 11 až 20 atd.

Existují tři proměnné, které určují, jaké záznamy se mají načíst a jak se má stránkovací rozhraní vykreslit:

  • Spustit row Index index prvního řádku na stránce dat k zobrazení; tento index lze vypočítat vynásobením indexu stránky záznamy, které se mají zobrazit na stránce, a přidáním jednoho indexu. Například při stránkování záznamů 10 najednou, pro první stránku (jejíž index stránky je 0) je index počátečního řádku 0 × 10 + 1 nebo 1; Pro druhou stránku (jejíž index stránky je 1) je index počátečního řádku 1 × 10 + 1 nebo 11.
  • Maximální počet řádků maximální počet záznamů, které se mají zobrazit na stránce. Tato proměnná se označuje jako maximální počet řádků, protože na poslední stránce může být vráceno méně záznamů, než je velikost stránky. Například při stránkování 81 produktů po 10 záznamech na stránku bude devátá a poslední stránka obsahovat pouze jeden záznam. Žádná stránka ale nebude zobrazovat více záznamů, než je hodnota Maximální počet řádků.
  • Total RecordPočítejte celkový počet stránkovaných záznamů. I když tato proměnná není potřebná k určení, které záznamy se mají načíst pro danou stránku, diktuje stránkovací rozhraní. Pokud se například stránkuje 81 produktů, stránkovací rozhraní ví, že v uživatelském rozhraní stránkování zobrazí devět čísel stránek.

Při výchozím stránkování se index počátečního řádku vypočítá jako součin indexu stránky a velikost stránky plus jedna, zatímco maximální počet řádků je jednoduše velikost stránky. Vzhledem k tomu, že výchozí stránkování načte všechny záznamy z databáze při vykreslování libovolné stránky dat, index pro každý řádek je známý, takže přechod na počáteční řádek indexu řádků je triviální úkol. Navíc je snadno dostupný celkový počet záznamů, protože se jedná jednoduše o počet záznamů v tabulce DataTable (nebo jakýkoli objekt, který se používá k uložení výsledků databáze).

Vzhledem k proměnným Spustit index řádků a Maximální počet řádků musí implementace vlastního stránkování vracet pouze přesnou podmnožinu záznamů počínaje počátečním indexem řádků a až do maximálního počtu řádků záznamů. Vlastní stránkování má dvě výzvy:

  • Musíme být schopni efektivně přidružit index řádků ke každému řádku v celých stránkovaných datech, abychom mohli začít vracet záznamy v zadaném indexu počátečního řádku.
  • Musíme zadat celkový počet záznamů, které jsou stránkovány

V následujících dvou krocích prozkoumáme skript SQL potřebný k reakci na tyto dvě výzvy. Kromě skriptu SQL budeme také muset implementovat metody v dal a BLL.

Krok 2: Vrácení celkového počtu stránkovaných záznamů

Než prozkoumáme, jak načíst přesnou podmnožinu záznamů pro zobrazenou stránku, podívejme se nejprve na to, jak vrátit celkový počet stránkovaných záznamů. Tyto informace jsou potřeba ke správné konfiguraci uživatelského rozhraní stránkování. Celkový počet záznamů vrácených konkrétním dotazem SQL je možné získat pomocí COUNT agregační funkce. Například k určení celkového počtu záznamů v Products tabulce můžeme použít následující dotaz:

SELECT COUNT(*)
FROM Products

Pojďme do dal přidat metodu, která vrátí tyto informace. Konkrétně vytvoříme metodu DAL s názvem TotalNumberOfProducts() , která provede SELECT výše uvedený příkaz.

Začněte otevřením Northwind.xsd souboru Typed DataSet ve App_Code/DAL složce. Potom klikněte pravým tlačítkem na ProductsTableAdapter tlačítko v Designer a zvolte Přidat dotaz. Jak jsme viděli v předchozích kurzech, umožní nám to přidat novou metodu do DAL, která při vyvolání provede konkrétní příkaz SQL nebo uloženou proceduru. Stejně jako u našich metod TableAdapter v předchozích kurzech se v tomto případě rozhodnete použít příkaz SQL ad hoc.

Použití ad hoc příkazu SQL

Obrázek 1: Použití ad hoc příkazu SQL

Na další obrazovce můžeme určit, jaký typ dotazu se má vytvořit. Vzhledem k tomu, že tento dotaz vrátí jednu skalární hodnotu, zvolí SELECT celkový počet záznamů v Products tabulce možnost, která vrátí hodnotu singe.

Konfigurace dotazu pro použití příkazu SELECT, který vrací jednu hodnotu

Obrázek 2: Konfigurace dotazu pro použití příkazu SELECT, který vrací jednu hodnotu

Po určení typu dotazu, který se má použít, musíme dále zadat dotaz.

Použití dotazu SELECT COUNT(*) FROM Products

Obrázek 3: Použití dotazu SELECT COUNT(*) FROM Products

Nakonec zadejte název metody. Jak je uvedeno výše, pojďme použít TotalNumberOfProducts.

Pojmenujte metodu DAL TotalNumberOfProducts.

Obrázek 4: Pojmenování metody DAL TotalNumberOfProducts

Po kliknutí na Dokončit průvodce přidá metodu TotalNumberOfProducts do dal. Skalární návratové metody v dal vracejí typy s možnou hodnotou null v případě, že výsledek dotazu SQL je NULL. Náš COUNT dotaz však vždy vrátí hodnotu, která neníNULL . Bez ohledu na to vrátí metoda DAL celé číslo s možnou hodnotou null.

Kromě metody DAL potřebujeme také metodu v BLL. ProductsBLL Otevřete soubor třídy a přidejte metoduTotalNumberOfProducts, která jednoduše volá metodu DAL sTotalNumberOfProducts:

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

Metoda DAL s TotalNumberOfProducts vrací celé číslo s možnou hodnotou null, ale vytvořili jsme metodu ProductsBLL třídy s TotalNumberOfProducts , aby vrátila standardní celé číslo. Proto potřebujeme, aby ProductsBLL metoda třídy s TotalNumberOfProducts vrátila část hodnoty celého čísla s možnou hodnotou null vrácenou metodou DAL s TotalNumberOfProducts . Volání GetValueOrDefault() vrátí hodnotu celočíselného čísla s možnou hodnotou null, pokud existuje. Pokud je nullcelé číslo s možnou hodnotou null, vrátí výchozí celočíselnou hodnotu 0.

Krok 3: Vrácení přesné podmnožině záznamů

Naším dalším úkolem je vytvořit metody v dal a BLL, které přijímají proměnné Start Row Index a Maximum Rows probírané dříve a vrátí příslušné záznamy. Než to uděláme, podívejme se nejprve na potřebný skript SQL. Výzvou, před kterou stojíme, je, že musíme být schopni efektivně přiřadit index každému řádku v celém stránkovaném výsledku, abychom mohli vrátit pouze ty záznamy počínaje indexem počátečního řádku (a až do maximálního počtu záznamů).

To není problém, pokud už v tabulce databáze existuje sloupec, který slouží jako index řádků. Na první pohled se můžeme domnívat, že Products pole tabulky ProductID s bude stačit, protože první součin má ProductID hodnotu 1, druhý 2 atd. Odstranění produktu ale zanechává mezeru v sekvenci, což tento přístup vynuluje.

K efektivnímu přidružení indexu řádků k datům, která se mají procházet stránkou, se používají dvě obecné techniky, které umožňují načtení přesné podmnožiny záznamů:

  • Pomocí SQL Server 2005 s ROW_NUMBER() Klíčové slovo nové pro SQL Server 2005, ROW_NUMBER() klíčové slovo přidruží hodnocení ke každému vráceným záznamu na základě nějakého pořadí. Toto pořadí lze použít jako index řádku pro každý řádek.

  • Použití proměnné tabulky a SET ROWCOUNT SQL Server příkaz s SET ROWCOUNT lze použít k určení celkového počtu záznamů, které má dotaz zpracovat před ukončením; Proměnné tabulky jsou místní proměnné T-SQL, které můžou obsahovat tabulková data, podobné dočasným tabulkám. Tento přístup funguje stejně dobře s Microsoft SQL Server 2005 a SQL Server 2000 (zatímco ROW_NUMBER() tento přístup funguje pouze s SQL Server 2005).

    Cílem je vytvořit proměnnou tabulky, která obsahuje IDENTITY sloupec a sloupce pro primární klíče tabulky, jejíž data jsou stránkována. Dále se obsah tabulky, jejíž data jsou stránkována, vypíše do proměnné tabulky, čímž se přidružuje sekvenční index řádků (prostřednictvím IDENTITY sloupce) pro každý záznam v tabulce. Jakmile je proměnná tabulky naplněna, SELECT je možné provést příkaz pro proměnnou tabulky, který je spojený s podkladovou tabulkou, aby se vytáhly konkrétní záznamy. Příkaz SET ROWCOUNT slouží k inteligentnímu omezení počtu záznamů, které je potřeba vypsat do proměnné tabulky.

    Efektivita tohoto přístupu je založená na požadovaném čísle stránky, protože SET ROWCOUNT hodnota je přiřazena k hodnotě Index počátečního řádku plus maximální počet řádků. Při stránkování stránek s nízkými čísly, jako je několik prvních stránek dat, je tento přístup velmi efektivní. Při načítání stránky blízko konce ale vykazuje výchozí výkon podobný stránkování.

Tento kurz implementuje vlastní stránkování pomocí klíčového ROW_NUMBER() slova. Další informace o použití proměnné tabulky a SET ROWCOUNT techniky najdete v tématu Efektivní stránkování velkých objemů dat.

Klíčové ROW_NUMBER() slovo přidružené ke každému záznamu vráceným pro konkrétní pořadí pomocí následující syntaxe:

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

ROW_NUMBER() vrátí číselnou hodnotu, která určuje pořadí každého záznamu s ohledem na uvedené pořadí. Pokud například chcete zobrazit pořadí jednotlivých produktů seřazených od nejdražšího po nejmenší, můžeme použít následující dotaz:

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

Obrázek 5 ukazuje výsledky tohoto dotazu při spuštění v okně dotazu v sadě Visual Studio. Všimněte si, že produkty jsou seřazené podle ceny spolu s pořadím cen pro každý řádek.

Cenové pořadí je zahrnuto pro každý vrácený záznam.

Obrázek 5: Cenové pořadí je zahrnuto pro každý vrácený záznam

Poznámka

ROW_NUMBER()je jen jednou z mnoha nových funkcí pro řazení, které jsou k dispozici v SQL Server 2005. Podrobnější diskuzi o ROW_NUMBER()funkci spolu s dalšími funkcemi řazení najdete v tématu Vrácení seřazených výsledků pomocí Microsoft SQL Server 2005.

Při řazení výsledků podle zadaného ORDER BY sloupce v OVER klauzuli (UnitPriceve výše uvedeném příkladu) musí SQL Server výsledky seřadit. Tato operace je rychlá, pokud existuje skupinový index nad sloupci, podle kterého jsou výsledky seřazeny, nebo pokud existuje překrývající se index, ale jinak může být nákladnější. Pokud chcete zlepšit výkon dostatečně velkých dotazů, zvažte přidání nes clusterovaného indexu pro sloupec, podle kterého jsou výsledky seřazené. Podrobnější informace o aspektech výkonu najdete v tématu Řazení funkcí a výkonu v SQL Server 2005.

Informace o pořadí vrácené nástrojem ROW_NUMBER() nelze v klauzuli WHERE použít přímo. K vrácení výsledku ROW_NUMBER() však lze použít odvozenou tabulku, která se pak může zobrazit v klauzuli WHERE . Například následující dotaz používá odvozenou tabulku k vrácení sloupců ProductName a UnitPrice spolu s ROW_NUMBER() výsledkem a pak použije WHERE klauzuli, která vrátí pouze ty produkty, jejichž cenové pořadí je mezi 11 a 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

Když tento koncept rozšíříme o trochu dál, můžeme tento přístup využít k načtení konkrétní stránky dat s požadovanými hodnotami indexu počátečního řádku a maximálního počtu řádků:

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>)

Poznámka

Jak uvidíme později v tomto kurzu, StartRowIndex hodnota zadaná objektem ObjectDataSource se indexuje od nuly, zatímco ROW_NUMBER() hodnota vrácená SQL Server 2005 se indexuje od hodnoty 1. Proto klauzule WHERE vrátí záznamy, u kterých PriceRank je přísně větší než StartRowIndex a menší než nebo rovna StartRowIndex + MaximumRows.

Teď, když jsme probrali, jak ROW_NUMBER() se dá použít k načtení konkrétní stránky dat vzhledem k hodnotám Index počátečního řádku a Maximální počet řádků, musíme teď tuto logiku implementovat jako metody v DAL a BLL.

Při vytváření tohoto dotazu musíme určit pořadí, podle kterého budou výsledky seřazeny. let s sort the products by their name in abecedně. To znamená, že s implementací vlastního stránkování v tomto kurzu nebudeme moct vytvořit vlastní stránkovanou sestavu, kterou lze také seřadit. V dalším kurzu ale uvidíme, jak se takové funkce dají poskytovat.

V předchozí části jsme vytvořili metodu DAL jako ad hoc příkaz SQL. Analyzátor T-SQL v sadě Visual Studio používaný průvodcem TableAdapter se bohužel nelíbí OVER syntaxi používané ROW_NUMBER() funkcí. Proto musíme tuto metodu DAL vytvořit jako uloženou proceduru. V nabídce Zobrazení vyberte Průzkumníka serveru (nebo stiskněte Ctrl+Alt+S) a rozbalte NORTHWND.MDF uzel. Pokud chcete přidat novou uloženou proceduru, klikněte pravým tlačítkem na uzel Uložené procedury a zvolte Přidat novou uloženou proceduru (viz obrázek 6).

Přidání nové uložené procedury pro stránkování v produktech

Obrázek 6: Přidání nové uložené procedury pro stránkování v produktech

Tato uložená procedura by měla přijímat dva celočíselné vstupní parametry @startRowIndex@maximumRows a používat ROW_NUMBER() funkci seřazenou podle ProductName pole, která vrátí pouze řádky větší než zadaná @startRowIndex hodnota a menší než nebo rovno @startRowIndex + @maximumRow s. Do nové uložené procedury zadejte následující skript a kliknutím na ikonu Uložit přidejte uloženou proceduru do databáze.

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 vytvoření uložené procedury si ji chvíli otestujte. V Průzkumníku GetProductsPaged serveru klikněte pravým tlačítkem na název uložené procedury a zvolte možnost Execute (Spustit). Visual Studio vás pak vyzve k zadání vstupních parametrů @startRowIndex a ( @maximumRow viz Obrázek 7). Zkuste různé hodnoty a prohlédněte si výsledky.

Zadejte hodnotu pro <span class=@startRowIndex and @maximumRows Parameters" />

Obrázek 7: Zadání hodnoty parametrů @startRowIndex a @maximumRows

Po výběru těchto hodnot vstupních parametrů se v okně Výstup zobrazí výsledky. Obrázek 8 ukazuje výsledky při předání hodnoty 10 parametrů i @startRowIndex@maximumRows .

Vrátí se záznamy, které by se zobrazily na druhé stránce dat.

Obrázek 8: Vrátí se záznamy, které se zobrazí na druhé stránce dat (kliknutím zobrazíte obrázek v plné velikosti).

Po vytvoření této uložené procedury jsme připraveni vytvořit metodu ProductsTableAdapter . Northwind.xsd Otevřete Typed DataSet(Typed DataSet), klikněte pravým tlačítkem na ProductsTableAdaptera zvolte možnost Přidat dotaz. Místo vytvoření dotazu pomocí ad hoc příkazu SQL ho vytvořte pomocí existující uložené procedury.

Vytvoření metody DAL pomocí existující uložené procedury

Obrázek 9: Vytvoření metody DAL pomocí existující uložené procedury

V dalším kroku se zobrazí výzva k výběru uložené procedury, která se má vyvolat. V rozevíracím seznamu vyberte uloženou GetProductsPaged proceduru.

V seznamu Drop-Down vyberte uloženou proceduru GetProductsPaged.

Obrázek 10: Výběr uložené procedury GetProductsPaged ze seznamu Drop-Down

Na další obrazovce se pak zobrazí dotaz, jaký druh dat uložená procedura vrací: tabulková data, jedna hodnota nebo žádná hodnota. Vzhledem k tomu, že uložená procedura GetProductsPaged může vrátit více záznamů, indikujte, že vrací tabulková data.

Označuje, že uložená procedura vrací tabulková data.

Obrázek 11: Indikuje, že uložená procedura vrací tabulková data

Nakonec uveďte názvy metod, které chcete vytvořit. Stejně jako v našich předchozích kurzech pokračujte a vytvořte metody pomocí vyplnění datové tabulky i vrácení datové tabulky. Pojmenujte první metodu FillPaged a druhou GetProductsPagedmetodu .

Pojmenujte metody FillPaged a GetProductsPaged.

Obrázek 12: Pojmenujte metody FillPaged a GetProductsPaged.

Kromě vytvoření metody DAL, která vrací konkrétní stránku produktů, potřebujeme tuto funkci také poskytnout v BLL. Stejně jako dal metoda, BLL s GetProductsPaged metoda musí přijmout dva celočíselné vstupy pro zadání počátečního řádku indexu a maximální řádky, a musí vrátit pouze ty záznamy, které spadají do zadaného rozsahu. Vytvořte takovou metodu BLL ve třídě ProductsBLL, která pouze volá metodu DAL s GetProductsPaged, například takto:

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

Pro vstupní parametry metody BLL můžete použít libovolný název, ale jak uvidíme za chvíli, volba použití startRowIndex a maximumRows ušetří nám další část práce při konfiguraci ObjectDataSource pro použití této metody.

Krok 4: Konfigurace objektu ObjectDataSource pro použití vlastního stránkování

Po dokončení metod BLL a DAL pro přístup k určité podmnožině záznamů jsme připraveni vytvořit ovládací prvek GridView, který stránky prochází jeho podkladové záznamy pomocí vlastního stránkování. Začněte tím, že EfficientPaging.aspx stránku otevřete ve PagingAndSorting složce, přidáte na stránku Objekt GridView a nakonfigurujete ji tak, aby používala nový ovládací prvek ObjectDataSource. V našich minulých kurzech jsme často měli ObjectDataSource nakonfigurovaný tak, aby používal metodu ProductsBLL třídy s GetProducts . Tentokrát ale chceme místo toho použít metodu GetProductsPaged , protože GetProducts metoda vrací všechny produkty v databázi, zatímco GetProductsPaged vrací jenom určitou podmnožinu záznamů.

Konfigurace ObjectDataSource pro použití třídy ProductsBLL s GetProductsPaged Metoda

Obrázek 13: Konfigurace objektu ObjectDataSource pro použití třídy ProductsBLL s metodou GetProductsPaged

Vzhledem k tomu, že vytváříme Objekt GridView jen pro čtení, nastavte rozevírací seznam metody na kartách INSERT, UPDATE a DELETE na (Žádné).

Dále průvodce ObjectDataSource zobrazí výzvu k zadání zdrojů GetProductsPaged hodnot metod s startRowIndex a maximumRows vstupních parametrů. Tyto vstupní parametry budou ve skutečnosti nastaveny GridView automaticky, takže jednoduše nechte zdroj nastavený na None a klikněte na Dokončit.

Nechejte zdroje vstupních parametrů na hodnotě Žádné.

Obrázek 14: Nechejte zdroje vstupních parametrů jako žádné.

Po dokončení průvodce ObjectDataSource bude GridView obsahovat BoundField nebo CheckBoxField pro každé z datových polí produktu. Nebojte se přizpůsobit vzhled GridView podle potřeby. Rozhodl(a) jsem se zobrazit pouze ProductName, , SupplierNameCategoryName, QuantityPerUnita UnitPrice BoundFields. Také nakonfigurujte GridView tak, aby podporoval stránkování zaškrtnutím políčka Povolit stránkování v jeho inteligentní značce. Po těchto změnách by deklarativní značky GridView a ObjectDataSource měly vypadat podobně jako následující:

<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>

Pokud však navštívíte stránku prostřednictvím prohlížeče, GridView není místo, kde se nachází.

Objekt GridView se nezobrazuje.

Obrázek 15: Objekt GridView se nezobrazuje

Objekt GridView chybí, protože ObjectDataSource aktuálně používá hodnotu 0 jako hodnoty pro vstupní parametry a GetProductsPagedstartRowIndexmaximumRows . Proto výsledný dotaz SQL nevrací žádné záznamy, a proto GridView se nezobrazí.

Abychom to napravili, musíme nakonfigurovat ObjectDataSource tak, aby používal vlastní stránkování. Můžete to provést v následujících krocích:

  1. Nastavte vlastnost ObjectDataSource na EnablePagingtruetuto hodnotu označuje objektu ObjectDataSource, že musí předat SelectMethod dva další parametry: jeden pro zadání indexu počátečního řádku (StartRowIndexParameterName) a jeden pro zadání maximálního počtu řádků (MaximumRowsParameterName).
  2. Nastavte ObjectDataSource s StartRowIndexParameterName a MaximumRowsParameterName Vlastnosti. VlastnostiStartRowIndexParameterName a MaximumRowsParameterName označují názvy vstupních parametrů předaných do objektu SelectMethod pro účely vlastního stránkování. Ve výchozím nastavení jsou startIndexRow tyto názvy parametrů a maximumRows, což je důvod, proč jsem při vytváření GetProductsPaged metody v BLL použil tyto hodnoty pro vstupní parametry. Pokud jste se rozhodli pro metodu BLL GetProductsPaged použít jiné názvy parametrů, například startIndex a maxRows, museli byste odpovídajícím způsobem nastavit vlastnosti ObjectDataSource StartRowIndexParameterName s a MaximumRowsParameterName (například startIndex pro StartRowIndexParameterName a maxRows pro MaximumRowsParameterName).
  3. Nastavte Vlastnost ObjectDataSource s SelectCountMethod na Název metody, která vrací celkový počet záznamů, které jsou stránkovány (TotalNumberOfProducts), vzpomeňte si, že ProductsBLL metoda třídy s TotalNumberOfProducts vrátí celkový počet záznamů stránkovaných prostřednictvím metody DAL, která spustí SELECT COUNT(*) FROM Products dotaz. Tyto informace jsou vyžadovány ObjectDataSource ke správnému vykreslení stránkovací rozhraní.
  4. startRowIndex Odeberte elementy a maximumRows<asp:Parameter> z deklarativní značky ObjectDataSource při konfiguraci ObjectDataSource prostřednictvím průvodce, Sada Visual Studio automaticky přidala dva <asp:Parameter> prvky pro GetProductsPaged vstupní parametry metody. EnablePaging Nastavením na truebudou tyto parametry předány automaticky. Pokud se také zobrazí v deklarativní syntaxi, ObjectDataSource se pokusí předat čtyři parametry metodě GetProductsPaged a dva parametry metoděTotalNumberOfProducts. Pokud zapomenete odebrat tyto <asp:Parameter> prvky, při návštěvě stránky v prohlížeči se zobrazí chybová zpráva typu ObjectDataSource 'ObjectDataSource1' could not find a non-generic method 'TotalNumberOfProducts' that has parameters: startRowIndex, maximumRows.

Po provedení těchto změn by deklarativní syntaxe ObjectDataSource měla vypadat takto:

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

Všimněte si, že byly nastaveny EnablePaging vlastnosti a SelectCountMethod a <asp:Parameter> prvky byly odebrány. Obrázek 16 ukazuje snímek obrazovky okno Vlastnosti po provedení těchto změn.

Pokud chcete použít vlastní stránkování, nakonfigurujte ovládací prvek ObjectDataSource.

Obrázek 16: Pokud chcete použít vlastní stránkování, nakonfigurujte ovládací prvek ObjectDataSource.

Po provedení těchto změn přejděte na tuto stránku prostřednictvím prohlížeče. Měli byste vidět 10 produktů seřazených abecedně. Chvíli si prohlédněte data po jednotlivých stránkách. I když z pohledu koncového uživatele není žádný vizuální rozdíl mezi výchozím stránkováním a vlastním stránkováním, vlastní stránkování efektivněji stránkuje velké objemy dat, protože načítá pouze ty záznamy, které je potřeba zobrazit pro danou stránku.

Data seřazená podle názvu produktu se stránkují pomocí vlastního stránkování.

Obrázek 17: Data seřazená podle názvu produktu jsou stránkována pomocí vlastního stránkování (kliknutím zobrazíte obrázek v plné velikosti).

Poznámka

Při vlastním stránkování je hodnota počtu stránek vrácená ObjectDataSource SelectCountMethod uložena ve stavu zobrazení GridView. Jiné proměnné PageIndexGridView , , EditIndexSelectedIndex, DataKeys kolekce a tak dále jsou uloženy ve stavu ovládacího prvku, který je trvalý bez ohledu na hodnotu vlastnosti GridViewEnableViewState. Vzhledem k tomu, že PageCount hodnota je zachována napříč postbacky pomocí stavu zobrazení, při použití stránkovací rozhraní, které obsahuje odkaz, který vás přesměruje na poslední stránku, je nutné, aby stav zobrazení GridView byl povolen. (Pokud vaše stránkovací rozhraní neobsahuje přímý odkaz na poslední stránku, můžete stav zobrazení zakázat.)

Kliknutí na odkaz poslední stránky způsobí zpětné odeslání a dá GridView pokyn k aktualizaci jeho PageIndex vlastnosti. Pokud kliknete na odkaz poslední stránky, Objekt GridView přiřadí jeho PageIndex vlastnost k hodnotě o jednu menší, než je jeho PageCount vlastnost. Když je stav zobrazení zakázaný, PageCount hodnota se v rámci zpětného odeslání ztratí a PageIndex místo toho se přiřadí maximální celočíselná hodnota. Dále se Objekt GridView pokusí určit index počátečního řádku vynásobením PageSize vlastností a PageCount . Výsledkem je, OverflowException že produkt překračuje maximální povolenou celočíselnou velikost.

Implementace vlastního stránkování a řazení

Naše aktuální vlastní implementace stránkování vyžaduje, aby pořadí, podle kterého jsou data stránkována, bylo při vytváření GetProductsPaged uložené procedury zadáno staticky. Možná jste si však všimli, že inteligentní značka GridView obsahuje kromě možnosti Povolit stránkování také zaškrtávací políčko Povolit řazení. Přidání podpory řazení do objektu GridView s naší aktuální vlastní implementací stránkování bohužel seřadí pouze záznamy na aktuálně zobrazené stránce dat. Pokud například gridView nakonfigurujete tak, aby podporoval také stránkování, a pak při zobrazení první stránky dat seřadíte podle názvu produktu v sestupném pořadí, změní se pořadí produktů na stránce 1. Jak ukazuje obrázek 18, takové ukazuje Carnarvon Tigers jako první produkt při řazení v obráceném abecedním pořadí, což ignoruje 71 dalších produktů, které přicházejí po Carnarvon Tigers, abecedně; Při řazení jsou brány v úvahu pouze ty záznamy na první stránce.

Seřadí se jenom data zobrazená na aktuální stránce.

Obrázek 18: Seřadí se pouze data zobrazená na aktuální stránce (kliknutím zobrazíte obrázek v plné velikosti).

Řazení se vztahuje pouze na aktuální stránku dat, protože k řazení dochází po načtení dat z metody BLL s GetProductsPaged a tato metoda vrací pouze tyto záznamy pro konkrétní stránku. Aby bylo možné správně implementovat řazení, musíme předat výraz sort metodě GetProductsPaged , aby data bylo možné před vrácením konkrétní stránky dat správně seřadit. V dalším kurzu se dozvíme, jak toho dosáhnout.

Implementace vlastního stránkování a odstranění

Pokud povolíte funkci odstranění v objektu GridView, jehož data jsou stránkována pomocí vlastních technik stránkování, zjistíte, že při odstranění posledního záznamu z poslední stránky GridView zmizí místo toho, aby odpovídajícím způsobem zmenšoval GridView s PageIndex. Pokud chcete tuto chybu reprodukovat, povolte odstranění právě vytvořeného kurzu. Přejděte na poslední stránku (strana 9), kde byste měli vidět jeden produkt, protože jsme se stránkovat přes 81 produktů, 10 produktů najednou. Odstraňte tento produkt.

Po odstranění posledního produktu by gridView měl automaticky přejít na osmou stránku a tato funkce se zobrazí s výchozím stránkováním. S vlastním stránkováním však po odstranění posledního produktu na poslední stránce GridView jednoduše zmizí z obrazovky úplně. Přesný důvod , proč k tomu dochází, je trochu nad rámec tohoto kurzu; Podrobnosti o zdroji tohoto problému najdete v části Odstranění posledního záznamu na poslední stránce z objektu GridView s vlastním stránkováním . V souhrnu je příčinou následující posloupnost kroků, které jsou provedeny GridView při kliknutí na tlačítko Odstranit:

  1. Odstranění záznamu
  2. Získání odpovídajících záznamů, které se mají zobrazit pro zadané PageIndex a PageSize
  3. Zkontrolujte, jestli PageIndex hodnota nepřekračuje počet stránek dat ve zdroji dat. Pokud ano, automaticky se sníží vlastnost GridView s PageIndex .
  4. Vytvoření vazby příslušné stránky dat k objektu GridView pomocí záznamů získaných v kroku 2

Problém pramení ze skutečnosti, že v kroku 2 PageIndex se při načítání zobrazených záznamů stále PageIndex používá poslední stránka, jejíž jediný záznam byl právě odstraněn. Proto se v kroku 2 nevrátí žádné záznamy, protože poslední stránka dat už neobsahuje žádné záznamy. Potom v kroku 3 GridView zjistí, že jeho PageIndex vlastnost je větší než celkový počet stránek ve zdroji dat (protože jsme odstranili poslední záznam na poslední stránce), a proto sníží jeho PageIndex vlastnost. V kroku 4 GridView se pokusí vytvořit vazbu na data načtená v kroku 2; v kroku 2 však nebyly vráceny žádné záznamy, takže výsledkem je prázdný Objekt GridView. Při výchozím stránkování se tento problém neobjeví, protože v kroku 2 jsou všechny záznamy načteny ze zdroje dat.

K vyřešení tohoto problému máme dvě možnosti. Prvním je vytvoření obslužné rutiny události pro obslužnou rutinu události GridView, RowDeleted která určuje, kolik záznamů bylo zobrazeno na stránce, která byla právě odstraněna. Pokud by existoval pouze jeden záznam, pak právě odstraněný záznam musel být poslední a musíme dekrementovat GridView s PageIndex. Samozřejmě chceme aktualizovat PageIndex pouze v případě, že operace odstranění byla skutečně úspěšná, což se dá určit zajištěním, že e.Exception vlastnost je null.

Tento přístup funguje, protože aktualizuje PageIndex následující krok 1, ale před krokem 2. Proto se v kroku 2 vrátí příslušná sada záznamů. K tomu použijte následující kód:

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);
}

Alternativním alternativním řešením je vytvořit obslužnou rutinu události objectDataSource a RowDeleted nastavit AffectedRows vlastnost na hodnotu 1. Po odstranění záznamu v kroku 1 (ale před opětovným načtením dat v kroku 2) GridView aktualizuje jeho PageIndex vlastnost, pokud operace ovlivnila jeden nebo více řádků. AffectedRows Vlastnost objectDataSource však není nastavena, a proto je tento krok vynechán. Jedním ze způsobů, jak tento krok provést, je nastavit vlastnost ručně AffectedRows , pokud se operace odstranění úspěšně dokončí. To lze provést pomocí kódu, jako je následující:

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;
}

Kód pro obě tyto obslužné rutiny událostí najdete v příkladu EfficientPaging.aspx ve třídě kódu na pozadí.

Porovnání výkonu výchozího a vlastního stránkování

Vzhledem k tomu, že vlastní stránkování načítá pouze potřebné záznamy, zatímco výchozí stránkování vrací všechny záznamy pro každou zobrazenou stránku, je jasné, že vlastní stránkování je efektivnější než výchozí stránkování. O kolik efektivnější je ale vlastní stránkování? Jaký druh zvýšení výkonu lze dosáhnout přechodem z výchozího stránkování na vlastní stránkování?

Bohužel, neexistuje jedna velikost pro všechny odpovědi. Zvýšení výkonu závisí na několika faktorech. Mezi dva nejvýznamnější patří počet stránkovaných záznamů a zatížení databázového serveru a komunikační kanály mezi webovým serverem a databázovým serverem. U malých tabulek s několika desítkami záznamů může být rozdíl ve výkonu zanedbatelný. U velkých tabulek s tisíci a stovkami tisíc řádků je ale rozdíl ve výkonu akutní.

Můj článek "Vlastní stránkování v ASP.NET 2.0 s SQL Server 2005" obsahuje některé testy výkonnosti, které jsem provedl, abych ukázal rozdíly ve výkonu mezi těmito dvěma technikami stránkování při stránkování v databázové tabulce s 50 000 záznamy. V těchto testech jsem prozkoumal čas spuštění dotazu na úrovni SQL Server (pomocí SQL Profileru) a na stránce ASP.NET pomocí funkcí trasování ASP.NET s. Mějte na paměti, že tyto testy byly spuštěny na vývojovém poli s jedním aktivním uživatelem, a proto jsou nevědecké a nenapodobují typické vzorce zatížení webu. Bez ohledu na to výsledky ukazují relativní rozdíly v době provádění výchozího a vlastního stránkování při práci s dostatečně velkými objemy dat.

Prům. doba trvání (s) Čte
Výchozí stránkování SQL Profileru 1.411 383
Vlastní stránkování SQL Profileru 0.002 29
Výchozí trasování stránkování ASP.NET 2.379
Trasování vlastního stránkování ASP.NET 0.029

Jak vidíte, načtení konkrétní stránky dat vyžadovalo v průměru o 354 čtení méně a dokončení za zlomek času. Na ASP.NET stránce byla vlastní stránka schopna vykreslit téměř 1/100času , který trvalo při použití výchozího stránkování.

Souhrn

K implementaci výchozího stránkování stačí zaškrtnout políčko Povolit stránkování v inteligentní značce webového ovládacího prvku dat, ale taková jednoduchost je za cenu výkonu. Při výchozím stránkování platí, že když uživatel požádá o libovolnou stránku dat, vrátí se všechny záznamy, i když se může zobrazit jen malý zlomek z nich. Pro boj s touto režií na výkon nabízí ObjectDataSource alternativní možnost stránkování vlastní stránkování.

I když se vlastní stránkování zlepšuje při výchozích problémech s výkonem stránkování tím, že načítá pouze ty záznamy, které je potřeba zobrazit, je více zapojené implementovat vlastní stránkování. Nejprve je potřeba napsat dotaz, který správně (a efektivně) přistupuje ke konkrétní podmnožině požadovaných záznamů. Toho lze dosáhnout mnoha způsoby; V tomto kurzu jsme se zabývali použitím nové ROW_NUMBER() funkce SQL Server 2005 k seřaďte výsledky a pak vrátíte jenom ty výsledky, jejichž pořadí spadá do zadaného rozsahu. Kromě toho musíme přidat prostředky k určení celkového počtu stránkovaných záznamů. Po vytvoření těchto metod DAL a BLL musíme také nakonfigurovat ObjectDataSource tak, aby mohl určit celkový počet záznamů, které jsou stránkovány, a správně předat počáteční index řádku a maximální počet řádků hodnoty BLL.

I když implementace vlastního stránkování vyžaduje několik kroků a není tak jednoduchá jako výchozí stránkování, vlastní stránkování je při stránkování dostatečně velkých objemů dat nezbytné. Jak ukázaly zkoumané výsledky, vlastní stránkování může z doby vykreslování ASP.NET stránky krátit sekundy a může odlehčovat zatížení databázového serveru o jeden nebo více řádů.

Všechno nejlepší na programování!

O autorovi

Scott Mitchell, autor sedmi knih o ASP/ASP.NET a zakladatel 4GuysFromRolla.com, pracuje s webovými technologiemi Microsoftu od roku 1998. Scott pracuje jako nezávislý konzultant, školitel a spisovatel. Jeho nejnovější kniha je Sams Teach Yourself ASP.NET 2.0 in 24 Hours. Můžete ho zastihnout na mitchell@4GuysFromRolla.comadrese . nebo prostřednictvím jeho blogu, který najdete na adrese http://ScottOnWriting.NET.