Notitie
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen u aan te melden of de directory te wijzigen.
Voor toegang tot deze pagina is autorisatie vereist. U kunt proberen de mappen te wijzigen.
door Scott Mitchell
De standaardoptie voor paginering van een besturingselement voor gegevenspresentatie is ongeschikt bij het werken met grote hoeveelheden gegevens, omdat het onderliggende besturingselement voor gegevensbronnen alle records ophaalt, zelfs wanneer slechts een subset van de gegevens wordt weergegeven. In dergelijke omstandigheden moeten we overschakelen naar aangepaste paging.
Introductie
Zoals we in de vorige zelfstudie hebben besproken, kan paging op twee manieren worden geïmplementeerd:
- Standaard paging kan worden geïmplementeerd door simpelweg de optie Paging inschakelen in de smart tag van het gegevenswebbesturing te controleren; echter wordt bij het bekijken van een gegevenspagina, haalt ObjectDataSource alle records op, ook al wordt er slechts een subset van deze records weergegeven op de pagina
- Aangepaste paginering verbetert de prestaties van standaard paginering door alleen de records op te halen uit de database die moeten worden weergegeven voor de specifieke pagina met gegevens die door de gebruiker zijn aangevraagd; aangepaste paginering vereist echter wat meer inspanning om te implementeren dan standaard paginering
Door het gemak van de implementatie schakelt u gewoon een selectievakje in en bent u klaar! standaard paging is een aantrekkelijke optie. De naïeve benadering bij het ophalen van alle records maakt het echter een onaanvaardbare keuze bij het paglen van voldoende grote hoeveelheden gegevens of voor sites met veel gelijktijdige gebruikers. In dergelijke omstandigheden moeten we omschakelen naar aangepaste paging om een responsief systeem te bieden.
De uitdaging van aangepaste paging is het schrijven van een query die de exacte set records retourneert die nodig zijn voor een bepaalde pagina met gegevens. Gelukkig biedt Microsoft SQL Server 2005 een nieuw trefwoord voor classificatieresultaten, waarmee we een query kunnen schrijven waarmee de juiste subset van records efficiënt kan worden opgehaald. In deze zelfstudie ziet u hoe u dit nieuwe SQL Server 2005-trefwoord gebruikt om aangepaste paging te implementeren in een GridView-besturingselement. Hoewel de gebruikersinterface voor aangepaste paginering identiek is aan die voor standaard paginering, kan het stapje van de ene pagina naar de volgende met aangepaste paginering meerdere ordes van grootte sneller zijn dan standaard paginering.
Opmerking
De exacte prestatiewinst die wordt weergegeven door aangepaste paging, is afhankelijk van het totale aantal records dat wordt gepagineerd en de belasting die op de databaseserver wordt geplaatst. Aan het einde van deze zelfstudie bekijken we enkele ruwe metrische gegevens die de voordelen van prestaties laten zien die zijn verkregen via aangepaste paging.
Stap 1: Informatie over het aangepaste pagingsproces
Wanneer u door gegevens bladert, zijn de exacte records die op een pagina worden weergegeven, afhankelijk van de pagina met de gegevens die worden aangevraagd en het aantal records dat per pagina wordt weergegeven. Stel dat we door de 81 producten willen bladeren, waarbij 10 producten per pagina worden weergegeven. Wanneer we de eerste pagina bekijken, willen we producten 1 tot en met 10; bij het bekijken van de tweede pagina zouden we geïnteresseerd zijn in producten 11 tot en met 20, enzovoort.
Er zijn drie variabelen die bepalen welke records moeten worden opgehaald en hoe de pagineringinterface moet worden weergegeven.
- Start Rijindex de index van de eerste rij op de pagina met gegevens die moeten worden weergegeven; deze index kan worden berekend door de pagina-index te vermenigvuldigen met de records die per pagina moeten worden weergegeven en er een toe te voegen. Wanneer u bijvoorbeeld voor de eerste pagina (waarvan de pagina-index 0 is) door records 10 per keer bladert, is de index van de beginrij 0 * 10 + 1 of 1; voor de tweede pagina (waarvan de pagina-index 1 is), is de index van de beginrij 1 * 10 + 1 of 11.
- Maximum aantal rijen het maximum aantal records dat per pagina moet worden weergegeven. Deze variabele wordt maximumrijen genoemd, omdat er voor de laatste pagina mogelijk minder records worden geretourneerd dan het paginaformaat. Wanneer u bijvoorbeeld door de 81 producten bladert met 10 vermeldingen per pagina, heeft de negende en laatste pagina slechts één vermelding. Op geen pagina worden meer records weergegeven dan het maximum aantal rijen.
- Total Record Count het totale aantal records dat wordt doorlopen. Hoewel deze variabele niet nodig is om te bepalen welke records moeten worden opgehaald voor een bepaalde pagina, bepaalt deze de paging-interface. Als er bijvoorbeeld 81 producten worden gepagineerd, weet de pagineringinterface dat er negen paginanummers in de paginering-UI weergegeven moeten worden.
Bij standaard paging wordt de beginrijindex berekend als het product van de pagina-index en het paginaformaat plus één, terwijl de maximumrijen gewoon het paginaformaat zijn. Aangezien bij het renderen van elke gegevenspagina alle records uit de database worden opgehaald, is de index voor elke rij bekend, waardoor het verplaatsen naar de Beginrij-index een triviale taak is. Bovendien is het totale aantal records direct beschikbaar, omdat het gewoon het aantal records in de gegevenstabel is (of welk object wordt gebruikt voor het opslaan van de databaseresultaten).
Gezien de variabelen voor de index van de beginrij en de maximumrijen, moet een aangepaste paging-implementatie alleen de exacte subset van records retourneren die beginnen bij de index van de beginrij en maximaal het maximum aantal records. Aangepaste paging biedt twee uitdagingen:
- We moeten op een efficiënte manier een rijindex kunnen koppelen aan elke rij in de gehele gepaginaarde gegevensset, zodat we records kunnen teruggeven vanaf de opgegeven begin-rijindex.
- We moeten het totale aantal records opgeven dat wordt gepaginad via
In de volgende twee stappen onderzoeken we het SQL-script dat nodig is om te reageren op deze twee uitdagingen. Naast het SQL-script moeten we ook methoden implementeren in de DAL en BLL.
Stap 2: retourneert het totale aantal records dat wordt gepaginad
Voordat we kijken hoe we de exacte subset van records voor de weergegeven pagina kunnen opvragen, gaan we eerst bekijken hoe we het totaal aantal te pagineren records kunnen terugkrijgen. Deze informatie is nodig om de gebruikersinterface voor paginering correct te configureren. Het totale aantal records dat door een bepaalde SQL-query wordt geretourneerd, kan worden verkregen met behulp van de COUNT statistische functie. Als u bijvoorbeeld het totale aantal records in de Products tabel wilt bepalen, kunnen we de volgende query gebruiken:
SELECT COUNT(*)
FROM Products
Laten we een methode toevoegen aan onze DAL die deze informatie retourneert. In het bijzonder zullen we een DAL-methode maken genaamd TotalNumberOfProducts(), die de bovenstaande instructie SELECT uitvoert.
Begin met het openen van het Northwind.xsd getypte DataSet-bestand in de App_Code/DAL map. Klik vervolgens met de rechtermuisknop op de ProductsTableAdapter Designer en kies Query toevoegen. Zoals we in de vorige zelfstudies hebben gezien, kunnen we hiermee een nieuwe methode toevoegen aan de DAL die, wanneer deze wordt aangeroepen, een bepaalde SQL-instructie of opgeslagen procedure uitvoert. Net als bij onze TableAdapter-methoden in eerdere zelfstudies, kiest u voor deze methode voor het gebruik van een ad-hoc SQL-instructie.
Afbeelding 1: Een ad-hoc SQL-instructie gebruiken
In het volgende scherm kunnen we opgeven welk type query moet worden gemaakt. Aangezien deze query een enkele scalaire waarde retourneert, namelijk het totale aantal records in de Products-tabel, kiest u de optie SELECT die een enkele waarde retourneert.
Afbeelding 2: De query configureren voor het gebruik van een SELECT-instructie die één waarde retourneert
Nadat u het type query hebt aangegeven dat moet worden gebruikt, moet u de query vervolgens opgeven.
Afbeelding 3: Gebruik de SELECT COUNT(*) FROM Products-query
Geef ten slotte de naam op voor de methode. Zoals hierboven vermeld, laten we gebruiken TotalNumberOfProducts.
Afbeelding 4: Geef de DAL-methode TotalNumberOfProducts een naam
Nadat u op Voltooien hebt geklikt, wordt de TotalNumberOfProducts methode aan de DAL toegevoegd. De scalaire retourmethoden in de DAL retourneren null-typen, voor het geval het resultaat van de SQL-query is NULL. Onze COUNT query retourneert altijd een niet-null waardeNULL; de DAL-methode retourneert echter een nullable geheel getal.
Naast de DAL-methode hebben we ook een methode in de BLL nodig. Open het ProductsBLL klassebestand en voeg een TotalNumberOfProducts methode toe die eenvoudigweg de DAL-methode TotalNumberOfProducts aanroept:
Public Function TotalNumberOfProducts() As Integer
Return Adapter.TotalNumberOfProducts().GetValueOrDefault()
End Function
De DAL-methode TotalNumberOfProducts retourneert een nullable geheel getal. We hebben echter de ProductsBLL-klasse en diens TotalNumberOfProducts-methode zo gemaakt dat er een standaard geheel getal wordt geretourneerd. Daarom moeten we ervoor zorgen dat de ProductsBLL methode klasse s TotalNumberOfProducts het waardegedeelte van het nullable gehele getal retourneert dat door de DAL-methode TotalNumberOfProducts wordt geretourneerd. De aanroep naar GetValueOrDefault() retourneert de waarde van het nullable integer, als dit bestaat; is het nullable integer null, dan retourneert het echter de standaardaarde voor integers, 0.
Stap 3: de exacte subset van records retourneren
De volgende taak is het maken van methoden in de DAL en BLL die de variabelen Beginrijindex en Maximumrijen accepteren die eerder zijn besproken en de juiste records retourneren. Voordat we dat doen, gaan we eerst kijken naar het benodigde SQL-script. De uitdaging waarmee we te maken hebben, is dat we efficiënt een index moeten kunnen toewijzen aan elke rij in de volledige resultaten die worden gepaginad, zodat we alleen die records kunnen retourneren die beginnen bij de index van de beginrij (en tot het maximum aantal records).
Dit is geen uitdaging als er al een kolom in de databasetabel is die als rijindex fungeert. Op het eerste gezicht kunnen we denken dat het veld van de Products tabel ProductID voldoende is, omdat het eerste product een 1 heeft, de tweede een 2, enzovoort ProductID. Als u een product verwijdert, blijft er echter een hiaat in de reeks achter, waardoor deze benadering ongeldig wordt gemaakt.
Er zijn twee algemene technieken die worden gebruikt om een rijindex efficiënt te koppelen aan de gegevens om door te bladeren, waardoor de precieze subset van records kan worden opgehaald:
SQL Server 2005 s gebruiken
ROW_NUMBER()Trefwoord nieuw in SQL Server 2005, hetROW_NUMBER()trefwoord koppelt een classificatie aan elke geretourneerde record op basis van een bepaalde volgorde. Deze rangschikking kan worden gebruikt als een rijindex voor elke rij.Een tabelvariabele gebruiken en
SET ROWCOUNTSQL Server-instructieSET ROWCOUNTkan worden gebruikt om op te geven hoeveel totaal records een query moet verwerken voordat deze wordt beëindigd; tabelvariabelen zijn lokale T-SQL-variabelen die tabelgegevens kunnen bevatten, vergelijkbaar met tijdelijke tabellen. Deze benadering werkt net zo goed met zowel Microsoft SQL Server 2005 als SQL Server 2000 (terwijl deROW_NUMBER()benadering alleen werkt met SQL Server 2005).Het idee hier is om een tabelvariabele te maken die een
IDENTITYkolom en kolommen bevat voor de primaire sleutels van de tabel waarvan de gegevens worden gepaginad. Vervolgens wordt de inhoud van de tabel waarvan de gegevens worden gepaginad, in de tabelvariabele gedumpt, waardoor een sequentiële rijindex (via deIDENTITYkolom) voor elke record in de tabel wordt gekoppeld. Zodra de tabelvariabele is ingevuld, kan eenSELECTinstructie voor de tabelvariabele, die is gekoppeld aan de onderliggende tabel, worden uitgevoerd om de specifieke records uit te trekken. DeSET ROWCOUNTinstructie wordt gebruikt om het aantal records dat in de tabelvariabele moet worden gedumpt op intelligente wijze te beperken.De efficiëntie van deze benadering is gebaseerd op het paginanummer dat wordt aangevraagd, omdat de
SET ROWCOUNTwaarde wordt toegewezen aan de waarde van de beginrijindex plus de maximumrijen. Bij het paglen van pagina's met weinig nummers, zoals de eerste paar pagina's met gegevens, is deze methode zeer efficiënt. Er worden echter standaardprestaties weergegeven die lijken op die van paging bij het ophalen van een pagina dichtbij het einde.
Deze handleiding implementeert aangepaste paging door het ROW_NUMBER() trefwoord te gebruiken. Zie SET ROWCOUNT meer informatie over het gebruik van de tabelvariabele en -techniek.
Het ROW_NUMBER() trefwoord dat is gekoppeld aan een rangschikking voor elke record die wordt geretourneerd via een bepaalde volgorde met behulp van de volgende syntaxis:
SELECT columnList,
ROW_NUMBER() OVER(orderByClause)
FROM TableName
ROW_NUMBER() retourneert een numerieke waarde die de rangschikking aangeeft voor elke record met betrekking tot de aangegeven volgorde. Als u bijvoorbeeld de rangschikking voor elk product wilt zien, gesorteerd van het duurst naar het minst, kunnen we de volgende query gebruiken:
SELECT ProductName, UnitPrice,
ROW_NUMBER() OVER(ORDER BY UnitPrice DESC) AS PriceRank
FROM Products
In afbeelding 5 ziet u de resultaten van deze query wanneer u het queryvenster in Visual Studio doorloopt. Houd er rekening mee dat de producten per prijs worden besteld, samen met een prijsrang voor elke rij.
Afbeelding 5: De prijsrang is opgenomen voor elke geretourneerde record
Opmerking
ROW_NUMBER() is slechts een van de vele nieuwe classificatiefuncties die beschikbaar zijn in SQL Server 2005. Lees ROW_NUMBER() voor een uitgebreidere bespreking van, samen met de andere classificatiefuncties.
Wanneer u de resultaten rangschikt op basis van de opgegeven ORDER BY kolom in de OVER component (UnitPricein het bovenstaande voorbeeld), moet SQL Server de resultaten sorteren. Dit is een snelle bewerking als er een geclusterde index is op de kolom(en) waarop de resultaten worden gesorteerd of als er een dekkingsindex is, maar anders duurder kan zijn. U kunt de prestaties voor voldoende grote query's verbeteren door een niet-geclusterde index toe te voegen voor de kolom waarop de resultaten zijn gerangschikt. Zie Classificatiefuncties en prestaties in SQL Server 2005 voor een gedetailleerder overzicht van de prestatieoverwegingen.
De classificatiegegevens die door ROW_NUMBER() de component worden geretourneerd, kunnen niet rechtstreeks worden gebruikt in de WHERE component. Een afgeleide tabel kan echter worden gebruikt om het ROW_NUMBER() resultaat te retourneren, dat vervolgens in de WHERE component kan worden weergegeven. De volgende query maakt bijvoorbeeld gebruik van een afgeleide tabel om de kolommen ProductName en Prijs per eenheid te retourneren, samen met het ROW_NUMBER() resultaat, en gebruikt vervolgens een WHERE component om alleen die producten te retourneren waarvan de prijsrang tussen 11 en 20 ligt:
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
Als u dit concept verder uitbreidt, kunnen we deze methode gebruiken om een specifieke pagina met gegevens op te halen op basis van de gewenste waarden voor de beginrijindex en maximumrijen:
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>)
Opmerking
Zoals we verderop in deze zelfstudie zullen zien, wordt de StartRowIndex index van de ObjectDataSource geïndexeerd vanaf nul, terwijl de ROW_NUMBER() waarde die wordt geretourneerd door SQL Server 2005 vanaf 1 wordt geïndexeerd. Daarom retourneert de WHERE component die records waar PriceRank strikt groter dan StartRowIndex en kleiner dan of gelijk aan StartRowIndex + MaximumRowsis.
Nu we besproken hebben hoe ROW_NUMBER() kan worden gebruikt om een specifieke pagina met gegevens op te halen op basis van de beginrijindex en de maximumrijen, moeten we deze logica nu als methoden in de DAL en BLL implementeren.
Bij het maken van deze query moeten we bepalen op welke volgorde de resultaten worden gerangschikt; laten we de producten sorteren op hun naam in alfabetische volgorde. Dit betekent dat we met de implementatie van aangepaste paging in deze tutorial geen aangepast gepagineerd rapport kunnen maken dat ook kan worden gesorteerd. In de volgende tutorial zien we hoe dergelijke functionaliteit kan worden geboden.
In de vorige sectie hebben we de DAL-methode gemaakt als een ad-hoc SQL-instructie. Helaas is de T-SQL-parser in Visual Studio die wordt gebruikt door de wizard TableAdapter niet tevreden over de OVER syntaxis die door de ROW_NUMBER() functie wordt gebruikt. Daarom moeten we deze DAL-methode maken als een opgeslagen procedure. Selecteer Server Explorer in het menu Beeld (of druk op Ctrl+Alt+S) en vouw het NORTHWND.MDF knooppunt uit. Als u een nieuwe opgeslagen procedure wilt toevoegen, klikt u met de rechtermuisknop op het knooppunt Opgeslagen procedures en kiest u Een nieuwe opgeslagen procedure toevoegen (zie afbeelding 6).
Afbeelding 6: Voeg een nieuwe opgeslagen procedure toe voor het doorbladeren van producten
Deze opgeslagen procedure moet twee gehele getallen als invoerparameters accepteren, namelijk @startRowIndex en @maximumRows. Het moet de ROW_NUMBER()-functie gebruiken, geordend op het ProductName-veld, en alleen die rijen retourneren die groter zijn dan de opgegeven @startRowIndex en kleiner dan of gelijk aan @startRowIndex + @maximumRow. Voer het volgende script in de nieuwe opgeslagen procedure in en klik vervolgens op het pictogram Opslaan om de opgeslagen procedure toe te voegen aan de database.
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)
Nadat u de opgeslagen procedure hebt gemaakt, neemt u even de tijd om deze te testen. Klik met de rechtermuisknop op de naam van de GetProductsPaged opgeslagen procedure in Server Explorer en kies de optie Uitvoeren. Visual Studio vraagt u vervolgens om de invoerparameters @startRowIndex en @maximumRow s (zie afbeelding 7). Probeer verschillende waarden en bekijk de resultaten.
@startRowIndex en @maximumRows parameters" />
Afbeelding 7: Voer een waarde in voor de @startRowIndex en @maximumRows parameters
Nadat u deze invoerparameters hebt gekozen, worden de resultaten weergegeven in het uitvoervenster. In afbeelding 8 ziet u de resultaten wanneer er 10 wordt doorgegeven voor zowel de parameters @startRowIndex als @maximumRows.
Afbeelding 8: De records die op de tweede pagina met gegevens worden weergegeven, worden geretourneerd (klik om de volledige afbeelding weer te geven)
Nu deze opgeslagen procedure is gemaakt, zijn we klaar om de ProductsTableAdapter methode te maken. Open de Northwind.xsd Typed DataSet, klik met de rechtermuisknop in de ProductsTableAdapter en kies de optie 'Query toevoegen'. In plaats van de query te maken met behulp van een ad-hoc SQL-instructie, maakt u deze met behulp van een bestaande opgeslagen procedure.
Afbeelding 9: De DAL-methode maken met behulp van een bestaande opgeslagen procedure
Vervolgens wordt u gevraagd om de opgeslagen procedure te selecteren die moet worden aangeroepen. Kies de GetProductsPaged opgeslagen procedure in de vervolgkeuzelijst.
Afbeelding 10: Kies de opgeslagen procedure GetProductsPaged in de Drop-Down-lijst
In het volgende scherm wordt u gevraagd welk soort gegevens worden geretourneerd door de opgeslagen procedure: gegevens in tabelvorm, één waarde of geen waarde. Omdat de GetProductsPaged opgeslagen procedure meerdere records kan retourneren, geeft u aan dat deze tabellaire gegevens retourneert.
Afbeelding 11: Hiermee wordt aangegeven dat de opgeslagen procedure tabellaire gegevens retourneert
Geef ten slotte de namen aan van de methoden die u wilt maken. Net als bij onze vorige tutorials, ga door en maak methoden door zowel een DataTable te vullen als een DataTable te retourneren. Geef de eerste methode FillPaged een naam en de tweede GetProductsPaged.
Afbeelding 12: Geef de methoden FillPaged en GetProductsPaged een naam
Naast het maken van een DAL-methode om een bepaalde pagina met producten te retourneren, moeten we ook dergelijke functionaliteit bieden in de BLL. Net als bij de DAL-methode moet de methode BLL s GetProductsPaged twee gehele getallen accepteren voor het opgeven van de beginrijindex en maximumrijen, en moet alleen de records retourneren die binnen het opgegeven bereik vallen. Maak zo'n BLL-methode in de klasse ProductsBLL die slechts in de GETProductsPaged-methode van DAL's wordt aangeroepen, zoals:
<System.ComponentModel.DataObjectMethodAttribute( _
System.ComponentModel.DataObjectMethodType.Select, False)> _
Public Function GetProductsPaged(startRowIndex As Integer, maximumRows As Integer) _
As Northwind.ProductsDataTable
Return Adapter.GetProductsPaged(startRowIndex, maximumRows)
End Function
U kunt elke naam gebruiken voor de invoerparameters van de BLL-methode, maar zoals we binnenkort zullen zien, als u ervoor kiest om startRowIndex en maximumRows te gebruiken, bespaart ons dat extra werk bij het configureren van een ObjectDataSource die deze methode gebruikt.
Stap 4: De ObjectDataSource configureren voor het gebruik van aangepaste paging
Nu de BLL- en DAL-methoden voor toegang tot een bepaalde subset van records zijn voltooid, zijn we klaar om een GridView-control te maken die door de onderliggende records bladert met behulp van aangepaste paginering. Open eerst de EfficientPaging.aspx pagina in de PagingAndSorting map, voeg een GridView toe aan de pagina en configureer deze voor het gebruik van een nieuw ObjectDataSource-besturingselement. In onze vorige zelfstudies hadden we de ObjectDataSource vaak geconfigureerd om de ProductsBLL klasse's GetProducts methode te gebruiken. Deze keer willen we echter de GetProductsPaged methode gebruiken, omdat de GetProducts methode alle producten in de database retourneert, terwijl GetProductsPaged alleen een bepaalde subset van records wordt geretourneerd.
Afbeelding 13: De ObjectDataSource configureren voor het gebruik van de getProductsPaged-methode van de ProductsBLL-klasse
Aangezien we een alleen-lezen GridView maken, stel de vervolgkeuzelijst voor de methode in op de tabbladen INSERT, UPDATE en DELETE op (Geen).
Vervolgens vraagt de wizard ObjectDataSource ons om de bronnen van de GetProductsPaged methode- startRowIndex en maximumRows invoerparameterswaarden. Deze invoerparameters worden in feite automatisch door GridView ingesteld, dus laat de bron ingesteld op Geen en klik op Voltooien.
Afbeelding 14: laat de invoerparameterbronnen staan op Geen
Nadat de wizard ObjectDataSource is voltooid, bevat GridView een BoundField- of CheckBoxField-veld voor elk van de productgegevensvelden. U kunt het uiterlijk van GridView naar wens aanpassen. Ik heb ervoor gekozen om alleen de ProductName, CategoryName, , SupplierNameen QuantityPerUnitUnitPrice BoundFields weer te geven. Configureer ook de GridView om paginering te ondersteunen door het selectievakje 'Paginering inschakelen' in de smart tag aan te vinken. Na deze wijzigingen moeten de declaratieve markeringen GridView en ObjectDataSource er ongeveer als volgt uitzien:
<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>
Als u de pagina via een browser bezoekt, is de GridView echter niet waar u kunt vinden.
Afbeelding 15: De Rasterweergave wordt niet weergegeven
GridView ontbreekt omdat ObjectDataSource momenteel 0 gebruikt als de waarden voor zowel de GetProductsPagedstartRowIndexmaximumRows als de invoerparameters. Daarom retourneert de resulterende SQL-query geen records en wordt de GridView daarom niet weergegeven.
Om dit te verhelpen, moeten we de ObjectDataSource configureren voor het gebruik van aangepaste paging. Dit kan worden bereikt in de volgende stappen:
- Stel de eigenschap ObjectDataSource in op
EnablePagingtrue, wat aangeeft aan de ObjectDataSource dat het moet doorgeven aan de twee extra parameters: één om de startreeksindex (SelectMethod) op te geven en één om het maximaal aantal rijen (StartRowIndexParameterName) op te geven. -
Stel de ObjectDataSource-s
StartRowIndexParameterNameenMaximumRowsParameterNameeigenschappen dienovereenkomstig in, waarbij deStartRowIndexParameterNameenMaximumRowsParameterNameeigenschappen de namen van de invoerparameters aangeven die zijn doorgegeven aan deSelectMethodvoor aangepaste pagingdoeleinden. Deze parameternamen zijnstartIndexRowstandaard enmaximumRowsdaarom heb ik bij het maken van deGetProductsPagedmethode in de BLL deze waarden gebruikt voor de invoerparameters. Als u ervoor kiest om verschillende parameternamen te gebruiken voor de methode BLLGetProductsPaged, zoalsstartIndexenmaxRows, bijvoorbeeld, moet u de ObjectDataSource-sStartRowIndexParameterNameenMaximumRowsParameterNameeigenschappen dienovereenkomstig instellen (zoals startIndex voorStartRowIndexParameterNameen maxRows voorMaximumRowsParameterName). - Stel de eigenschap ObjectDataSource in
SelectCountMethodop de naam van de methode die het totale aantal records retourneert dat wordt gepagineerd door (TotalNumberOfProducts) en onthoud dat de methode van de deProductsBLLklasse het totale aantal records retourneert dat wordt gepagineerd met behulp van een DAL-methode die eenTotalNumberOfProductsquery uitvoert. Deze informatie is nodig voor de ObjectDataSource om de paging-interface correct weer te geven. -
Verwijder de
startRowIndexenmaximumRows<asp:Parameter>elementen uit de declaratieve markeringen van ObjectDataSource bij het configureren van de ObjectDataSource via de wizard. Visual Studio heeft automatisch twee<asp:Parameter>elementen toegevoegd voor de invoerparameters van deGetProductsPagedmethode. DoorEnablePagingoptruete zetten, worden deze parameters automatisch doorgegeven. Als ze ook in de declaratieve syntaxis verschijnen, zal de ObjectDataSource proberen vier parameters door te geven aan deGetProductsPaged-methode en twee parameters aan deTotalNumberOfProducts-methode. Als u deze<asp:Parameter>elementen vergeet te verwijderen, krijgt u bij het bezoeken van de pagina via een browser een foutbericht zoals: ObjectDataSource 'ObjectDataSource1' kan geen niet-algemene methode 'TotalNumberOfProducts' vinden die parameters heeft: startRowIndex, maximumRowRows.
Nadat u deze wijzigingen hebt aangebracht, moet de declaratieve syntaxis van ObjectDataSource er als volgt uitzien:
<asp:ObjectDataSource ID="ObjectDataSource1" runat="server"
OldValuesParameterFormatString="original_{0}" SelectMethod="GetProductsPaged"
TypeName="ProductsBLL" EnablePaging="True" SelectCountMethod="
TotalNumberOfProducts">
</asp:ObjectDataSource>
Houd er rekening mee dat de EnablePaging en SelectCountMethod eigenschappen zijn ingesteld en dat de <asp:Parameter> elementen zijn verwijderd. Afbeelding 16 toont een schermafbeelding van het venster Eigenschappen nadat deze wijzigingen zijn aangebracht.
Afbeelding 16: Om aangepaste paging te gebruiken, configureer het ObjectDataSource-besturingselement
Nadat u deze wijzigingen hebt aangebracht, gaat u naar deze pagina via een browser. U ziet nu 10 producten die alfabetisch zijn gesorteerd. Neem even de tijd om de gegevens één pagina tegelijk te doorlopen. Hoewel er geen visueel verschil is voor de eindgebruiker tussen standaardpagina's en aangepaste pagina's, werkt aangepaste paginering efficiënter door grote hoeveelheden gegevens, omdat alleen de records worden opgehaald die voor een bepaalde pagina moeten worden weergegeven.
Afbeelding 17: De gegevens, besteld op productnaam, worden gepagineerd met behulp van aangepaste paging (klik om de afbeelding op volledige grootte weer te geven)
Opmerking
Bij aangepaste paging wordt de waarde voor het aantal pagina's SelectCountMethod die door de ObjectDataSource wordt geretourneerd, opgeslagen in de weergavestatus van GridView. Andere GridView-variabelen, zoals de PageIndex, EditIndex, SelectedIndex, DataKeys-verzameling, enzovoort, worden opgeslagen in de controlestatus, die behouden blijft ongeacht de waarde van de EnableViewState-eigenschap van de GridView. Omdat de waarde behouden blijft voor postbacks met behulp van de weergavestatus, is het belangrijk dat de PageCount weergavestatus van GridView is ingeschakeld wanneer u een paging-interface gebruikt die een koppeling bevat die u naar de laatste pagina brengt. (Als uw pagineringsinterface geen directe koppeling naar de laatste pagina bevat, dan kunt u de weergavestatus uitschakelen.)
Door op de laatste paginakoppeling te klikken, wordt er een postback uitgevoerd en instructeert de GridView om zijn PageIndex-eigenschap bij te werken. Als op de laatste paginakoppeling wordt geklikt, wijst GridView de eigenschap toe PageIndex aan een waarde die kleiner is dan de PageCount eigenschap ervan. Als de weergavestatus is uitgeschakeld, gaat de PageCount waarde verloren tussen postbacks en wordt PageIndex in plaats daarvan de maximumwaarde voor gehele getallen toegewezen. Vervolgens probeert GridView de beginrijindex te bepalen door de PageSize en PageCount eigenschappen te vermenigvuldigen. Dit resulteert in een OverflowException omdat het product de maximaal toegestane gehele getallen overschrijdt.
Aangepaste paging en sortering implementeren
Onze huidige aangepaste paging-implementatie vereist dat de volgorde waarin de gegevens worden gepagineerd statisch worden opgegeven bij het maken van de GetProductsPaged opgeslagen procedure. Maar misschien hebt u opgemerkt dat de smart tag van GridView een selectievakje 'Sorteren inschakelen' bevat, naast de optie 'Paging inschakelen.' Helaas zal het toevoegen van sorteerondersteuning aan de GridView met onze huidige aangepaste paging-implementatie alleen de records op de momenteel bekeken gegevenspagina sorteren. Als u rasterweergave bijvoorbeeld zo configureert dat paging ook wordt ondersteund en vervolgens, wanneer u de eerste pagina met gegevens bekijkt, sorteert u op productnaam in aflopende volgorde, dan wordt de volgorde van de producten op pagina 1 omgekeerd. Zoals in afbeelding 18 wordt weergegeven, toont dit Carnarvon Tigers als het eerste product bij het sorteren in omgekeerde alfabetische volgorde, waarbij de 71 andere producten die na Carnarvon Tigers komen, alfabetisch worden genegeerd; alleen de records op de eerste pagina worden in de sortering overwogen.
Afbeelding 18: Alleen de gegevens die op de huidige pagina worden weergegeven, worden gesorteerd (klik om de afbeelding op volledige grootte weer te geven)
De sortering is alleen van toepassing op de huidige pagina met gegevens omdat de sortering plaatsvindt nadat de gegevens zijn opgehaald uit de methode BLL GetProductsPaged en deze methode retourneert alleen die records voor de specifieke pagina. Om de sortering correct te implementeren, moeten we de sorteerexpressie doorgeven aan de GetProductsPaged methode, zodat de gegevens op de juiste manier kunnen worden gerangschikt voordat de specifieke pagina met gegevens wordt geretourneerd. In onze volgende zelfstudie ziet u hoe u dit kunt doen.
Aangepaste paginering en verwijdering invoeren
Als u de functionaliteit voor verwijderen inschakelt in een GridView waarvan de gegevens worden gepagineerd met behulp van aangepaste pagingtechnieken, zult u merken dat wanneer u de laatste record van de laatste pagina verwijdert, de GridView verdwijnt in plaats van de GridView PageIndexop de juiste manier te verlagen. Als u deze fout wilt reproduceren, schakelt u verwijderen in voor de zelfstudie die we zojuist hebben gemaakt. Ga naar de laatste pagina (pagina 9), waar u één product moet zien, omdat we door 81 producten, 10 producten tegelijk bladeren. Verwijder dit product.
Bij het verwijderen van het laatste product moet de GridView automatisch naar de achtste pagina gaan en wordt dergelijke functionaliteit weergegeven met standaard paging. Met aangepaste paginering verdwijnt de GridView echter helemaal van het scherm na het verwijderen van dat laatste product op de laatste pagina. De precieze reden waarom dit gebeurt, valt iets buiten het bereik van deze zelfstudie; Zie Het verwijderen van de laatste record op de laatste pagina van een GridView met aangepaste paging voor de details op laag niveau over de bron van dit probleem. Kortom, dit komt door de volgende reeks stappen die door de GridView worden uitgevoerd wanneer op de knop Verwijderen wordt geklikt:
- De record verwijderen
- Haal de juiste records op die moeten worden weergegeven voor de opgegeven
PageIndexenPageSize - Controleer of het
PageIndexaantal pagina's met gegevens in de gegevensbron niet overschrijdt. Als dit wel het geval is, wordt de eigenschap GridViewPageIndexautomatisch afgebroken - Koppel de juiste pagina met gegevens aan de GridView met behulp van de records die zijn verkregen in stap 2
Het probleem komt voort uit het feit dat in stap 2 de PageIndex die wordt gebruikt bij het ophalen van de records die moeten worden weergegeven, nog steeds de PageIndex van de laatste pagina is wiens enige record zojuist is verwijderd. Daarom worden in stap 2 geen records geretourneerd omdat die laatste pagina met gegevens geen records meer bevat. In stap 3 beseft GridView dat zijn PageIndex-eigenschap groter is dan het totale aantal pagina's in de gegevensbron (omdat we het laatste record op de laatste pagina hebben verwijderd) en vermindert daarom zijn PageIndex-eigenschap. In stap 4 probeert GridView zichzelf te binden aan de gegevens die zijn opgehaald in stap 2; In stap 2 zijn echter geen records geretourneerd, wat resulteert in een lege GridView. Bij standaard paging wordt dit probleem niet weergegeven omdat in stap 2 alle records worden opgehaald uit de gegevensbron.
We hebben twee opties om dit op te lossen. De eerste is het maken van een gebeurtenis-handler voor de GridView-gebeurtenis-handler RowDeleted die bepaalt hoeveel records zijn weergegeven op de pagina die zojuist is verwijderd. Als er slechts één record was, moet de record die zojuist is verwijderd de laatste zijn en moeten we de GridView-s PageIndexverlagen. Natuurlijk willen we alleen een update uitvoeren op PageIndex als de verwijdering daadwerkelijk succesvol was, wat kan worden bepaald door ervoor te zorgen dat de e.Exception eigenschap null is.
Deze methode werkt omdat het PageIndex bijwerkt na stap 1, maar vóór stap 2. Daarom wordt in stap 2 de juiste set records geretourneerd. Gebruik hiervoor code als de volgende:
Protected Sub GridView1_RowDeleted(sender As Object, e As GridViewDeletedEventArgs) _
Handles GridView1.RowDeleted
' If we just deleted the last row in the GridView, decrement the PageIndex
If e.Exception Is Nothing AndAlso GridView1.Rows.Count = 1 Then
' we just deleted the last row
GridView1.PageIndex = Math.Max(0, GridView1.PageIndex - 1)
End If
End Sub
Een alternatieve tijdelijke oplossing is het maken van een gebeurtenis-handler voor de ObjectDataSource-gebeurtenis RowDeleted en het instellen van de AffectedRows eigenschap op een waarde van 1. Na het verwijderen van de record in stap 1 (maar voordat de gegevens in stap 2 worden opgehaald), wordt de eigenschap van GridView PageIndex bijgewerkt als een of meer rijen zijn beïnvloed door de bewerking. De AffectedRows eigenschap wordt echter niet ingesteld door de ObjectDataSource en daarom wordt deze stap weggelaten. Een manier om deze stap uit te voeren, is door de AffectedRows eigenschap handmatig in te stellen als de verwijderbewerking is voltooid. Dit kan worden bereikt met behulp van code als de volgende:
Protected Sub ObjectDataSource1_Deleted( _
sender As Object, e As ObjectDataSourceStatusEventArgs) _
Handles ObjectDataSource1.Deleted
' 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 TypeOf e.ReturnValue Is Boolean AndAlso CType(e.ReturnValue, Boolean) = True Then
e.AffectedRows = 1
End If
End Sub
De code voor beide gebeurtenis-handlers vindt u in de code-behind-klasse van het EfficientPaging.aspx voorbeeld.
De prestaties van standaard- en aangepaste paging vergelijken
Omdat aangepaste paging alleen de benodigde records ophaalt, terwijl standaard paging alle records retourneert voor elke pagina die wordt bekeken, is het duidelijk dat aangepaste paging efficiënter is dan standaard paging. Maar hoeveel efficiënter is aangepaste paging? Wat voor prestatieverbeteringen kunt u zien door over te stappen van standaard paging naar aangepaste paging?
Helaas is er hier geen enkel formaat dat bij alle antwoorden past. De prestatiewinst is afhankelijk van een aantal factoren, de meest prominente twee zijn het aantal records dat wordt gepaginad en de belasting op de databaseserver en communicatiekanalen tussen de webserver en databaseserver. Voor kleine tabellen met slechts enkele tientallen records is het prestatieverschil mogelijk te verwaarlozen. Voor grote tabellen, met duizenden tot honderdduizenden rijen, is het prestatieverschil echter acuut.
Een artikel van mij, 'Aangepaste paging in ASP.NET 2.0 met SQL Server 2005', bevat enkele prestatietests die ik heb uitgevoerd om de verschillen in prestaties tussen deze twee pagingtechnieken te vertonen bij het pagiëren van een databasetabel met 50.000 records. In deze tests heb ik zowel de tijd onderzocht om de query uit te voeren op SQL Server-niveau (met behulp van SQL Profiler) als op de ASP.NET pagina met behulp van de traceringsfuncties van ASP.NET. Houd er rekening mee dat deze tests werden uitgevoerd in mijn ontwikkelomgeving met één actieve gebruiker, en daarom onwetenschappelijk zijn uitgevoerd en geen typische laadpatronen voor websites repliceren. Hoe dan ook, de resultaten illustreren de relatieve verschillen in uitvoeringstijd voor standaard- en aangepaste paging bij het werken met voldoende grote hoeveelheden gegevens.
| Gemiddelde duur (sec) | Leest | |
|---|---|---|
| Standaard paginering SQL Profiler | 1.411 | 383 |
| Aangepaste paginering SQL Profiler | 0.002 | 29 |
| Standaard paginering ASP.NET trace | 2.379 | N.V.T. |
| Aangepaste ASP.NET-pagineringstrace | 0.029 | N.V.T. |
Zoals u kunt zien, kunt u een bepaalde pagina met gegevens ophalen die gemiddeld 354 minder leesbewerkingen nodig hebben en in een fractie van de tijd zijn voltooid. Op de ASP.NET pagina kon de aangepaste pagina worden weergegeven in bijna 1/100e van de tijd die nodig was bij het gebruik van standaardpaginering.
Samenvatting
Standaard paging is een makkie om te implementeren. Vink het selectievakje 'Paging inschakelen' aan in de infotag van de gegevens-webcontrole, maar een dergelijke eenvoud komt ten koste van de prestaties. Bij standaard paginering worden, wanneer een gebruiker een pagina met gegevens aanvraagt , alle records geretourneerd, ook al kan slechts een klein deel van de records worden weergegeven. Om deze prestatieoverhead te bestrijden, biedt ObjectDataSource een alternatieve pagingoptie voor aangepaste paging.
Hoewel aangepaste paginering prestatieproblemen van standaard paginering verbetert door alleen de records op te halen die moeten worden weergegeven, vergt de implementatie van aangepaste paginering meer inspanning. Eerst moet een query worden geschreven die correct (en efficiënt) toegang heeft tot de specifieke subset van aangevraagde records. Dit kan op verschillende manieren worden bereikt; de functie die we in deze zelfstudie hebben onderzocht, is het gebruik van de nieuwe ROW_NUMBER() functie van SQL Server 2005 om resultaten te rangschikken en vervolgens alleen die resultaten te retourneren waarvan de classificatie binnen een opgegeven bereik valt. Daarnaast moeten we een manier toevoegen om het totale aantal gegevens te bepalen dat wordt doorgebladerd. Nadat u deze DAL- en BLL-methoden hebt gemaakt, moeten we ook de ObjectDataSource configureren, zodat kan worden bepaald hoeveel totaalrecords worden gepaginad en de waarden voor de beginrijindex en maximumrijen correct kunnen worden doorgegeven aan de BLL.
Hoewel het implementeren van aangepaste paginering meerdere stappen vereist en lang niet zo eenvoudig is als standaard paginering, is het toch noodzakelijk wanneer er door zeer grote hoeveelheden gegevens gepagineerd moet worden. Zoals uit de onderzochte resultaten bleek, kan aangepaste paging seconden van de laadtijd van ASP.NET-pagina's verminderen en de belasting op de databaseserver met een of meerdere ordes van grootte verlichten.
Veel plezier met programmeren!
Over de auteur
Scott Mitchell, auteur van zeven ASP/ASP.NET-boeken en oprichter van 4GuysFromRolla.com, werkt sinds 1998 met Microsoft-webtechnologieën. Scott werkt als onafhankelijk consultant, trainer en schrijver. Zijn laatste boek is Sams Teach Yourself ASP.NET 2.0 in 24 uur. Hij kan worden bereikt op mitchell@4GuysFromRolla.com.