Suddivisione in pagine efficiente di grandi quantità di dati (VB)

di Scott Mitchell

Scarica il PDF

L'opzione di paging predefinita di un controllo presentazione dati non è adatta quando si utilizzano grandi quantità di dati, perché il controllo origine dati sottostante recupera tutti i record, anche se viene visualizzato solo un subset di dati. In tali circostanze, è necessario passare al paging personalizzato.

Introduzione

Come illustrato nell'esercitazione precedente, il paging può essere implementato in uno dei due modi seguenti:

  • Il paging predefinito può essere implementato semplicemente selezionando l'opzione Abilita paging nello smart tag del controllo Web dei dati; Tuttavia, ogni volta che si visualizza una pagina di dati, ObjectDataSource recupera tutti i record, anche se nella pagina viene visualizzato solo un subset di dati
  • Il paging personalizzato migliora le prestazioni del paging predefinito recuperando solo i record dal database che devono essere visualizzati per la pagina specifica dei dati richiesti dall'utente; Tuttavia, il paging personalizzato comporta un po 'più sforzo per implementare il paging predefinito

A causa della facilità di implementazione basta selezionare una casella di controllo e si è fatto di nuovo! il paging predefinito è un'opzione interessante. Il suo approccio ingenuo nel recupero di tutti i record, tuttavia, lo rende una scelta implausibile quando si esegue il paging attraverso quantità sufficientemente elevate di dati o per siti con molti utenti simultanei. In tali circostanze, è necessario passare al paging personalizzato per fornire un sistema reattivo.

La sfida del paging personalizzato consiste nel scrivere una query che restituisce il set preciso di record necessari per una determinata pagina di dati. Fortunatamente, Microsoft SQL Server 2005 fornisce una nuova parola chiave per i risultati di classificazione, che consente di scrivere una query in grado di recuperare in modo efficiente il subset di record appropriato. In questa esercitazione verrà illustrato come usare questa nuova parola chiave SQL Server 2005 per implementare il paging personalizzato in un controllo GridView. Anche se l'interfaccia utente per il paging personalizzato è identica a quella per il paging predefinito, l'esecuzione di un'istruzione da una pagina all'altra tramite il paging personalizzato può essere di diversi ordini di grandezza più veloci rispetto al paging predefinito.

Nota

Il miglioramento esatto delle prestazioni esposto dal paging personalizzato dipende dal numero totale di record sottoposti a paging e dal carico inserito nel server di database. Alla fine di questa esercitazione verranno esaminate alcune metriche approssimative che illustrano i vantaggi delle prestazioni ottenute tramite il paging personalizzato.

Passaggio 1: Informazioni sul processo di paging personalizzato

Quando si esegue il paging dei dati, i record precisi visualizzati in una pagina dipendono dalla pagina dei dati richiesti e dal numero di record visualizzati per pagina. Si supponga, ad esempio, di voler scorrere i 81 prodotti, visualizzando 10 prodotti per pagina. Quando si visualizza la prima pagina, si vogliono prodotti da 1 a 10; quando visualizziamo la seconda pagina saremmo interessati a prodotti da 11 a 20 e così via.

Esistono tre variabili che determinano quali record devono essere recuperati e come deve essere eseguito il rendering dell'interfaccia di paging:

  • Start Row Index l'indice della prima riga nella pagina dei dati da visualizzare; questo indice può essere calcolato moltiplicando l'indice di pagina per i record da visualizzare per pagina e aggiungendo uno. Ad esempio, quando si esegue il paging tra record 10 alla volta, per la prima pagina (il cui indice di pagina è 0), l'indice di riga iniziale è 0 * 10 + 1 o 1; per la seconda pagina (il cui indice di pagina è 1), l'indice riga iniziale è 1 * 10 + 1 o 11.
  • Numero massimo di righe il numero massimo di record da visualizzare per pagina. Questa variabile viene definita righe massime perché per l'ultima pagina potrebbero essere restituiti meno record rispetto alle dimensioni della pagina. Ad esempio, quando si esegue il paging tra 81 prodotti 10 record per pagina, la nona e la pagina finale avranno un solo record. Nessuna pagina, tuttavia, mostrerà più record rispetto al valore Massimo righe.
  • Total Record Count il numero totale di record di cui viene eseguito il paging. Anche se questa variabile non è necessaria per determinare quali record recuperare per una determinata pagina, determina l'interfaccia di paging. Ad esempio, se sono presenti 81 prodotti sottoposti a paging, l'interfaccia di paging sa visualizzare nove numeri di pagina nell'interfaccia utente di paging.

Con il paging predefinito, l'indice riga iniziale viene calcolato come prodotto dell'indice di pagina e le dimensioni della pagina più uno, mentre le dimensioni massime delle righe sono semplicemente le dimensioni della pagina. Poiché il paging predefinito recupera tutti i record dal database quando si esegue il rendering di una pagina di dati, l'indice per ogni riga è noto, rendendo così semplice il passaggio alla riga Avvia indice di riga. Inoltre, il conteggio record totale è facilmente disponibile, poiché è semplicemente il numero di record in DataTable (o qualsiasi oggetto utilizzato per contenere i risultati del database).

Date le variabili Start Row Index e Maximum Rows, un'implementazione di paging personalizzata deve restituire solo il subset preciso di record a partire dall'indice di riga iniziale e fino al numero massimo di righe successive. Il paging personalizzato offre due sfide:

  • È necessario essere in grado di associare in modo efficiente un indice di riga a ogni riga di tutti i dati sottoposti a paging, in modo da poter iniziare a restituire record in corrispondenza dell'indice di riga iniziale specificato
  • È necessario fornire il numero totale di record sottoposti a paging

Nei due passaggi successivi verrà esaminato lo script SQL necessario per rispondere a queste due sfide. Oltre allo script SQL, sarà necessario implementare anche i metodi in DAL e BLL.

Passaggio 2: Restituzione del numero totale di record sottoposti a paging

Prima di esaminare come recuperare il sottoinsieme preciso di record per la pagina visualizzata, si esaminerà innanzitutto come restituire il numero totale di record di cui viene eseguito il paging. Queste informazioni sono necessarie per configurare correttamente l'interfaccia utente di paging. Il numero totale di record restituiti da una determinata query SQL può essere ottenuto usando la COUNT funzione di aggregazione. Ad esempio, per determinare il numero totale di record nella Products tabella, è possibile usare la query seguente:

SELECT COUNT(*)
FROM Products

Aggiungere un metodo a DAL che restituisce queste informazioni. In particolare, verrà creato un metodo DAL denominato TotalNumberOfProducts() che esegue l'istruzione SELECT illustrata in precedenza.

Per iniziare, aprire il Northwind.xsd file DataSet tipizzato nella App_Code/DAL cartella . Fare quindi clic con il ProductsTableAdapter pulsante destro del mouse sul nella Designer e scegliere Aggiungi query. Come illustrato nelle esercitazioni precedenti, ciò consentirà di aggiungere un nuovo metodo a DAL che, quando richiamato, eseguirà una particolare istruzione SQL o stored procedure. Come per i metodi TableAdapter nelle esercitazioni precedenti, per questo scegliere di usare un'istruzione SQL ad hoc.

Usare un'istruzione SQL ad hoc

Figura 1: Usare un'istruzione SQL ad hoc

Nella schermata successiva è possibile specificare il tipo di query da creare. Poiché questa query restituirà un singolo valore scalare, il numero totale di record nella Products tabella sceglie l'opzione SELECT che restituisce un valore singe.

Configurare la query per l'utilizzo di un'istruzione SELECT che restituisce un singolo valore

Figura 2: Configurare la query per l'uso di un'istruzione SELECT che restituisce un singolo valore

Dopo aver indicato il tipo di query da usare, è necessario specificare successivamente la query.

Usare la query SELECT COUNT(*) FROM Products

Figura 3: Usare la query SELECT COUNT(*) FROM Products

Infine, specificare il nome per il metodo . Come accennato in precedenza, è possibile usare TotalNumberOfProducts.

Denominare il metodo DAL TotalNumberOfProducts

Figura 4: Denominare il metodo DAL TotalNumberOfProducts

Dopo aver fatto clic su Fine, la procedura guidata aggiungerà il TotalNumberOfProducts metodo a DAL. I metodi scalari che restituiscono i tipi nullable dal, nel caso in cui il risultato della query SQL sia NULL. La COUNT query restituirà tuttavia sempre un valore nonNULL . Indipendentemente dal fatto che il metodo DAL restituisca un numero intero nullable.

Oltre al metodo DAL, è necessario anche un metodo nel BLL. Aprire il file di ProductsBLL classe e aggiungere un TotalNumberOfProducts metodo che chiama semplicemente al metodo dal TotalNumberOfProducts :

Public Function TotalNumberOfProducts() As Integer
    Return Adapter.TotalNumberOfProducts().GetValueOrDefault()
End Function

Il metodo DAL s TotalNumberOfProducts restituisce un numero intero nullable. È stato tuttavia creato il ProductsBLL metodo della TotalNumberOfProducts classe in modo che restituisca un numero intero standard. È quindi necessario che il ProductsBLL metodo della TotalNumberOfProducts classe restituisca la parte del valore dell'intero nullable restituito dal metodo DAL.TotalNumberOfProducts La chiamata a GetValueOrDefault() restituisce il valore dell'intero nullable, se esistente; se l'intero nullable è null, tuttavia, restituisce il valore intero predefinito 0.

Passaggio 3: Restituzione del subset preciso di record

L'attività successiva consiste nel creare metodi in DAL e BLL che accettano le variabili Start Row Index e Maximum Rows descritte in precedenza e restituiscono i record appropriati. Prima di eseguire questa operazione, esaminare prima di tutto lo script SQL necessario. La sfida è che dobbiamo essere in grado di assegnare in modo efficiente un indice a ogni riga di tutti i risultati sottoposti a paging, in modo da poter restituire solo i record a partire dall'indice di riga iniziale (e fino al numero massimo di record).

Questo non è un problema se esiste già una colonna nella tabella di database che funge da indice di riga. A prima vista si potrebbe pensare che il Products campo della ProductID tabella sarebbe sufficiente, come il primo prodotto ha ProductID di 1, il secondo un 2 e così via. Tuttavia, l'eliminazione di un prodotto lascia una distanza nella sequenza, annullando questo approccio.

Esistono due tecniche generali usate per associare in modo efficiente un indice di riga ai dati da scorrere, consentendo così il sottoinsieme preciso di record da recuperare:

  • Usando SQL Server 2005 s ROW_NUMBER() Keyword new to SQL Server 2005, la ROW_NUMBER() parola chiave associa una classificazione a ogni record restituito in base ad alcuni ordini. Questa classificazione può essere usata come indice di riga per ogni riga.

  • Uso di una variabile di tabella e SET ROWCOUNT SQL Server'istruzione s SET ROWCOUNT può essere usata per specificare il numero di record totali che una query deve elaborare prima di terminare; le variabili di tabella sono variabili T-SQL locali che possono contenere dati tabulari, simili a tabelle temporanee. Questo approccio funziona altrettanto bene con Microsoft SQL Server 2005 e SQL Server 2000 (mentre l'approccio ROW_NUMBER() funziona solo con SQL Server 2005).

    L'idea è creare una variabile di tabella con una IDENTITY colonna e colonne per le chiavi primarie della tabella i cui dati vengono sottoposti a paging. Successivamente, il contenuto della tabella i cui dati vengono sottoposti a paging viene sottoposto a dump nella variabile di tabella, associando quindi un indice di riga sequenziale (tramite la IDENTITY colonna) per ogni record nella tabella. Dopo aver popolato la variabile di tabella, è possibile eseguire un'istruzione SELECT nella variabile di tabella unita alla tabella sottostante per estrarre i record specifici. L'istruzione SET ROWCOUNT viene usata per limitare in modo intelligente il numero di record che devono essere scaricati nella variabile di tabella.

    L'efficienza di questo approccio si basa sul numero di pagina richiesto, in quanto al SET ROWCOUNT valore viene assegnato il valore di Indice riga iniziale più le righe massime. Quando si esegue il paging tra pagine con numeri bassi, ad esempio le prime pagine di dati, questo approccio è molto efficiente. Tuttavia, mostra prestazioni di paging predefinite simili al recupero di una pagina vicino alla fine.

Questa esercitazione implementa il paging personalizzato usando la ROW_NUMBER() parola chiave . Per altre informazioni sull'uso della variabile e SET ROWCOUNT della tecnica di tabella, vedere Un metodo più efficiente per il paging tramite set di risultati di grandi dimensioni.

Parola ROW_NUMBER() chiave associata a una classificazione a ogni record restituito su un ordine specifico usando la sintassi seguente:

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

ROW_NUMBER() restituisce un valore numerico che specifica la classificazione per ogni record in relazione all'ordinamento indicato. Ad esempio, per visualizzare la classificazione per ogni prodotto, ordinata dal più costoso al minimo, è possibile usare la query seguente:

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

La figura 5 mostra i risultati di questa query quando viene eseguita tramite la finestra di query in Visual Studio. Si noti che i prodotti vengono ordinati in base al prezzo, insieme a un rango di prezzo per ogni riga.

La classificazione dei prezzi è inclusa per ogni record restituito

Figura 5: La classificazione dei prezzi è inclusa per ogni record restituito

Nota

ROW_NUMBER()è solo una delle numerose nuove funzioni di classificazione disponibili in SQL Server 2005. Per una descrizione più approfondita di , insieme alle altre funzioni di ROW_NUMBER()classificazione, leggere ROW_NUMBER documentazione.

Quando si classificano i risultati in base alla colonna specificata ORDER BY nella OVER clausola (UnitPricenell'esempio precedente), SQL Server deve ordinare i risultati. Si tratta di un'operazione rapida se è presente un indice cluster sulle colonne per cui i risultati vengono ordinati o se è presente un indice di copertura, ma può essere più costoso in caso contrario. Per migliorare le prestazioni per query sufficientemente grandi, è consigliabile aggiungere un indice non cluster per la colonna in base al quale i risultati vengono ordinati in base a . Per informazioni più dettagliate sulle considerazioni sulle prestazioni, vedere Funzioni di classificazione e prestazioni in SQL Server 2005.

Le informazioni sulla classificazione restituite da ROW_NUMBER() non possono essere utilizzate direttamente nella WHERE clausola . Tuttavia, è possibile utilizzare una tabella derivata per restituire il ROW_NUMBER() risultato, che può quindi essere visualizzato nella WHERE clausola . Ad esempio, la query seguente usa una tabella derivata per restituire le colonne ProductName e UnitPrice, insieme al ROW_NUMBER() risultato, e quindi usa una WHERE clausola per restituire solo i prodotti il cui rango di prezzo è compreso tra 11 e 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

Estendendo ulteriormente questo concetto, è possibile usare questo approccio per recuperare una pagina specifica di dati in base ai valori di indice riga iniziale e massimo righe desiderati:

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

Nota

Come si vedrà più avanti in questa esercitazione, l'oggetto StartRowIndex fornito da ObjectDataSource viene indicizzato a partire da zero, mentre il ROW_NUMBER() valore restituito da SQL Server 2005 viene indicizzato a partire da 1. Pertanto, la WHERE clausola restituisce i record in cui PriceRank è strettamente maggiore di StartRowIndex e minore o uguale a StartRowIndex + MaximumRows.

Ora che è stato illustrato come ROW_NUMBER() usare per recuperare una determinata pagina di dati in base ai valori Start Row Index e Maximum Rows, è ora necessario implementare questa logica come metodi in DAL e BLL.

Quando si crea questa query, è necessario decidere l'ordinamento in base al quale verranno classificati i risultati; consente di ordinare i prodotti in base al nome in ordine alfabetico. Ciò significa che con l'implementazione di paging personalizzata in questa esercitazione non sarà possibile creare un report con paging personalizzato, che non sarà possibile ordinare. Nell'esercitazione successiva, tuttavia, si vedrà come è possibile fornire tali funzionalità.

Nella sezione precedente è stato creato il metodo DAL come istruzione SQL ad hoc. Sfortunatamente, il parser T-SQL in Visual Studio usato dalla procedura guidata TableAdapter non piace alla OVER sintassi usata dalla ROW_NUMBER() funzione . Pertanto, è necessario creare questo metodo DAL come stored procedure. Selezionare Esplora server dal menu Visualizza (o premere CTRL+ALT+S) ed espandere il NORTHWND.MDF nodo. Per aggiungere una nuova stored procedure, fare clic con il pulsante destro del mouse sul nodo Stored procedure e scegliere Aggiungi una nuova stored procedure (vedere la figura 6).

Aggiungere una nuova stored procedure per il paging dei prodotti

Figura 6: Aggiungere una nuova stored procedure per il paging dei prodotti

Questa stored procedure deve accettare due parametri @startRowIndex di input integer e @maximumRows usare la ROW_NUMBER() funzione ordinata dal ProductName campo, restituendo solo le righe maggiori dell'oggetto specificato @startRowIndex e minore o uguale a @startRowIndex + @maximumRow s. Immettere lo script seguente nella nuova stored procedure e quindi fare clic sull'icona Salva per aggiungere la stored procedure al 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)

Dopo aver creato la stored procedure, eseguire un test. Fare clic con il pulsante destro del mouse sul nome della GetProductsPaged stored procedure in Esplora server e scegliere l'opzione Esegui. Visual Studio richiederà quindi i parametri @startRowIndex di input e @maximumRow s (vedere la figura 7). Provare valori diversi ed esaminare i risultati.

Immettere un valore per <span class=@startRowIndex e @maximumRows parametri" />

Figura 7: Immettere un valore per i @startRowIndex parametri e @maximumRows

Dopo aver scelto questi valori dei parametri di input, nella finestra Output verranno visualizzati i risultati. La figura 8 mostra i risultati quando si passa 10 per entrambi i @startRowIndex parametri e @maximumRows .

I record visualizzati nella seconda pagina di dati vengono restituiti

Figura 8: Vengono restituiti i record visualizzati nella seconda pagina di dati (fare clic per visualizzare l'immagine a dimensione intera)

Dopo aver creato questa stored procedure, è possibile creare il ProductsTableAdapter metodo . Aprire il Northwind.xsd set di dati tipizzato, fare clic con il pulsante destro del ProductsTableAdaptermouse in e scegliere l'opzione Aggiungi query. Anziché creare la query usando un'istruzione SQL ad hoc, crearla usando una stored procedure esistente.

Creare il metodo DAL usando una stored procedure esistente

Figura 9: Creare il metodo DAL usando una stored procedure esistente

Verrà quindi richiesto di selezionare la stored procedure da richiamare. Selezionare la GetProductsPaged stored procedure dall'elenco a discesa.

Scegliere la stored procedure GetProductsPaged dall'elenco Drop-Down

Figura 10: Scegliere la stored procedure GetProductsPaged dall'elenco Drop-Down

La schermata successiva chiede quindi quale tipo di dati viene restituito dalla stored procedure: dati tabulari, un singolo valore o nessun valore. Poiché la GetProductsPaged stored procedure può restituire più record, indicare che restituisce dati tabulari.

Indicare che la stored procedure restituisce dati tabulari

Figura 11: Indicare che la stored procedure restituisce dati tabulari

Infine, indicare i nomi dei metodi che si desidera creare. Come per le esercitazioni precedenti, procedere e creare metodi usando sia Fill a DataTable che Return a DataTable. Denominare il primo metodo FillPaged e il secondo GetProductsPaged.

Denominare i metodi FillPaged e GetProductsPaged

Figura 12: Denominare i metodi FillPaged e GetProductsPaged

Oltre a creare un metodo DAL per restituire una determinata pagina di prodotti, è necessario fornire tali funzionalità nel BLL. Analogamente al metodo DAL, il metodo GetProductsPaged di BLL deve accettare due input integer per specificare l'indice riga iniziale e le righe massime e deve restituire solo i record che rientrano nell'intervallo specificato. Creare un metodo BLL di questo tipo nella classe ProductsBLL che richiama semplicemente nel metodo GETProductsPaged dal, come illustrato di seguito:

<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

È possibile usare qualsiasi nome per i parametri di input del metodo BLL, ma, come si vedrà a breve, scegliere di usare startRowIndex e maximumRows salvare da un bit di lavoro aggiuntivo durante la configurazione di ObjectDataSource per l'uso di questo metodo.

Passaggio 4: Configurazione di ObjectDataSource per l'uso del paging personalizzato

Con i metodi BLL e DAL per accedere a un determinato sottoinsieme di record completi, è possibile creare un controllo GridView che scorre i record sottostanti tramite il paging personalizzato. Per iniziare, aprire la EfficientPaging.aspx pagina nella PagingAndSorting cartella, aggiungere un controllo GridView alla pagina e configurarlo per l'uso di un nuovo controllo ObjectDataSource. Nelle esercitazioni precedenti è stato spesso configurato ObjectDataSource per l'uso del ProductsBLL metodo della GetProducts classe . Questa volta, tuttavia, si vuole usare il GetProductsPaged metodo , poiché il GetProducts metodo restituisce tutti i prodotti nel database, mentre GetProductsPaged restituisce solo un determinato subset di record.

Configurare ObjectDataSource per l'utilizzo del metodo GetProductsPaged della classe ProductsBLL

Figura 13: Configurare ObjectDataSource per l'uso del metodo GetProductsPaged della classe ProductsBLL

Poiché si sta creando un controllo GridView di sola lettura, impostare l'elenco a discesa del metodo nelle schede INSERT, UPDATE e DELETE su (Nessuno).

La procedura guidata ObjectDataSource richiede quindi le origini dei valori dei GetProductsPaged parametri di input e maximumRows del startRowIndex metodo. Questi parametri di input verranno effettivamente impostati automaticamente da GridView, quindi è sufficiente lasciare l'origine impostata su Nessuno e fare clic su Fine.

Lasciare Le origini dei parametri di input come Nessuna

Figura 14: Lasciare le origini dei parametri di input come Nessuna

Dopo aver completato la procedura guidata ObjectDataSource, GridView conterrà un campo BoundField o CheckBoxField per ognuno dei campi dati del prodotto. È possibile personalizzare l'aspetto di GridView in base alle esigenze. Ho scelto di visualizzare solo gli ProductNameoggetti , CategoryName, SupplierName, QuantityPerUnite UnitPrice BoundFields. Configurare anche GridView per supportare il paging selezionando la casella di controllo Abilita paging nello smart tag. Dopo queste modifiche, il markup dichiarativo GridView e ObjectDataSource dovrebbe essere simile al seguente:

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

Se si visita la pagina tramite un browser, tuttavia, GridView non è dove trovare.

GridView non viene visualizzato

Figura 15: GridView non visualizzato

GridView non è presente perché ObjectDataSource usa attualmente 0 come valori per entrambi i GetProductsPagedstartRowIndex parametri di input e maximumRows . Di conseguenza, la query SQL risultante non restituisce alcun record e pertanto gridView non viene visualizzata.

Per risolvere questo problema, è necessario configurare ObjectDataSource per l'uso del paging personalizzato. Questa operazione può essere eseguita nei passaggi seguenti:

  1. Impostare la proprietà trueObjectDataSource su EnablePaging questo indica a ObjectDataSource che deve passare ai SelectMethod due parametri aggiuntivi: uno per specificare l'indice di riga iniziale () e uno per specificare il valore Maximum Rows (StartRowIndexParameterNameMaximumRowsParameterName).
  2. Impostare ObjectDataSource s StartRowIndexParameterName e MaximumRowsParameterName Proprietà Di conseguenza le StartRowIndexParameterName proprietà e MaximumRowsParameterName indicano i nomi dei parametri di input passati a SelectMethod per scopi di paging personalizzati. Per impostazione predefinita, questi nomi di parametro sono startIndexRow e maximumRows, motivo per cui, quando si crea il GetProductsPaged metodo nel BLL, questi valori sono stati usati per i parametri di input. Se si sceglie di usare nomi di parametro diversi per il metodo BLL, GetProductsPaged ad esempio e maxRows, è necessario impostare di conseguenza le proprietà ObjectDataSource s StartRowIndexParameterName e MaximumRowsParameterName , ad esempio startIndex per StartRowIndexParameterName e maxRows per MaximumRowsParameterName.startIndex
  3. Impostare la proprietà ObjectDataSource s SelectCountMethod sul nome del metodo che restituisce il numero totale di record sottoposti a paging (TotalNumberOfProducts) ricordare che il ProductsBLL metodo della TotalNumberOfProducts classe restituisce il numero totale di record di cui viene eseguito il paging tramite un metodo DAL che esegue una SELECT COUNT(*) FROM Products query. Queste informazioni sono necessarie da ObjectDataSource per eseguire correttamente il rendering dell'interfaccia di paging.
  4. Rimuovere gli startRowIndex elementi e maximumRows<asp:Parameter> dal markup dichiarativo di ObjectDataSource durante la configurazione di ObjectDataSource tramite la procedura guidata, Visual Studio ha aggiunto automaticamente due <asp:Parameter> elementi per i GetProductsPaged parametri di input del metodo. Se si imposta EnablePaging su true, questi parametri verranno passati automaticamente. Se vengono visualizzati anche nella sintassi dichiarativa, ObjectDataSource tenterà di passare quattro parametri al GetProductsPaged metodo e due parametri al TotalNumberOfProducts metodo. Se si dimentica di rimuovere questi <asp:Parameter> elementi, quando si visita la pagina tramite un browser verrà visualizzato un messaggio di errore simile a : ObjectDataSource 'ObjectDataSource1' non è stato possibile trovare un metodo non generico 'TotalNumberOfProducts' con parametri: startRowIndex, maximumRows.

Dopo aver apportato queste modifiche, la sintassi dichiarativa di ObjectDataSource dovrebbe essere simile alla seguente:

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

Si noti che le EnablePaging proprietà e SelectCountMethod sono state impostate e che gli <asp:Parameter> elementi sono stati rimossi. La figura 16 mostra una schermata del Finestra Proprietà dopo aver apportato queste modifiche.

Per usare il paging personalizzato, configurare il controllo ObjectDataSource

Figura 16: Per usare il paging personalizzato, configurare il controllo ObjectDataSource

Dopo aver apportato queste modifiche, visitare questa pagina tramite un browser. Verranno visualizzati 10 prodotti elencati, ordinati in ordine alfabetico. Passare un momento per eseguire il passaggio dei dati una pagina alla volta. Anche se non esiste alcuna differenza visiva dal punto di vista dell'utente finale tra il paging predefinito e il paging personalizzato, il paging personalizzato di pagine in modo più efficiente tramite grandi quantità di dati perché recupera solo i record che devono essere visualizzati per una determinata pagina.

I dati, ordinati in base al nome del prodotto, vengono impaginati usando il paging personalizzato

Figura 17: i dati, ordinati in base al nome del prodotto, vengono impaginati usando paging personalizzato (fare clic per visualizzare l'immagine a dimensioni complete)

Nota

Con il paging personalizzato, il valore del conteggio delle pagine restituito dall'oggetto ObjectDataSource viene archiviato nello stato di visualizzazione di SelectCountMethod GridView. Altre variabili GridView, raccolta PageIndex, SelectedIndexEditIndexDataKeys , e così via vengono archiviate nello stato di controllo, che vengono mantenute indipendentemente dal valore della proprietà GridView.EnableViewState Poiché il PageCount valore viene mantenuto tra postback usando lo stato di visualizzazione, quando si usa un'interfaccia di paging che include un collegamento che consente di passare all'ultima pagina, è imperativo che lo stato di visualizzazione di GridView sia abilitato. Se l'interfaccia di paging non include un collegamento diretto all'ultima pagina, è possibile disabilitare lo stato di visualizzazione.

Facendo clic sull'ultimo collegamento della pagina viene generato un postback e viene indicato a GridView di aggiornarne la PageIndex proprietà. Se viene fatto clic sull'ultimo collegamento della pagina, GridView assegna la relativa PageIndex proprietà a un valore minore della relativa PageCount proprietà. Con lo stato di visualizzazione disabilitato, il PageCount valore viene perso tra postback e viene PageIndex assegnato il valore intero massimo. Successivamente, GridView tenta di determinare l'indice di riga iniziale moltiplicando le PageSize proprietà e PageCount . Ciò comporta un risultato poiché OverflowException il prodotto supera la dimensione massima consentita dell'intero.

Implementare paging e ordinamento personalizzati

L'implementazione di paging personalizzata corrente richiede che l'ordine in base al quale i dati vengano impaginati in modo statico durante la creazione della GetProductsPaged stored procedure. Tuttavia, è possibile notare che lo smart tag di GridView contiene una casella di controllo Abilita ordinamento oltre all'opzione Abilita paging. Purtroppo, l'aggiunta del supporto di ordinamento a GridView con l'implementazione del paging personalizzato corrente ordina solo i record nella pagina attualmente visualizzata dei dati. Ad esempio, se si configura GridView per supportare anche il paging e quindi, quando si visualizza la prima pagina dei dati, ordinare in base al nome del prodotto in ordine decrescente, invertire l'ordine dei prodotti nella pagina 1. Come illustrato nella figura 18, tali elementi mostrano Carnarvon Tigers come primo prodotto durante l'ordinamento alfabetico inverso, che ignora i 71 altri prodotti che provengono dopo Carnarvon Tigers, alfabeticamente; solo i record nella prima pagina vengono considerati nell'ordinamento.

Solo i dati visualizzati nella pagina corrente sono ordinati

Figura 18: solo i dati visualizzati nella pagina corrente sono ordinati (fare clic per visualizzare l'immagine full-size)

L'ordinamento si applica solo alla pagina corrente dei dati perché l'ordinamento si verifica dopo che i dati sono stati recuperati dal metodo BLL GetProductsPaged e questo metodo restituisce solo tali record per la pagina specifica. Per implementare correttamente l'ordinamento, è necessario passare l'espressione di ordinamento al GetProductsPaged metodo in modo che i dati possano essere classificati in modo appropriato prima di restituire la pagina specifica dei dati. Verrà illustrato come eseguire questa operazione nell'esercitazione successiva.

Implementazione di paging e eliminazione personalizzata

Se si abilita l'eliminazione delle funzionalità in GridView i cui dati vengono impaginati usando tecniche di paging personalizzate, si scoprirà che quando si elimina l'ultimo record dall'ultima pagina, GridView scompare anziché decrementare in modo appropriato GridView s PageIndex. Per riprodurre questo bug, abilitare l'eliminazione per l'esercitazione appena creata. Passare all'ultima pagina (pagina 9), dove si dovrebbe visualizzare un singolo prodotto poiché stiamo paging attraverso 81 prodotti, 10 prodotti alla volta. Eliminare questo prodotto.

Dopo l'eliminazione dell'ultimo prodotto, GridView dovrebbe passare automaticamente all'ottava pagina e tale funzionalità viene visualizzata con il paging predefinito. Con il paging personalizzato, tuttavia, dopo aver eliminato l'ultimo prodotto nell'ultima pagina, GridView scompare semplicemente dalla schermata. Il motivo preciso per cui ciò accade è un po' oltre l'ambito di questa esercitazione; vedere Eliminazione dell'ultimo record nell'ultima pagina da gridView con paging personalizzato per i dettagli di basso livello come origine di questo problema. In riepilogo è dovuto alla sequenza seguente di passaggi eseguiti da GridView quando viene fatto clic sul pulsante Elimina:

  1. Eliminare il record
  2. Ottenere i record appropriati da visualizzare per l'oggetto specificato PageIndex e PageSize
  3. Verificare che l'oggetto PageIndex non superi il numero di pagine di dati nell'origine dati; se lo fa, decrerere automaticamente la proprietà GridView PageIndex
  4. Associare la pagina appropriata dei dati a GridView usando i record ottenuti nel passaggio 2

Il problema deriva dal fatto che nel passaggio 2 l'oggetto PageIndex usato durante l'afferramento dei record da visualizzare è ancora quello PageIndex dell'ultima pagina il cui unico record è stato appena eliminato. Pertanto, nel passaggio 2 non vengono restituiti record poiché l'ultima pagina dei dati non contiene più record. Quindi, nel passaggio 3, GridView si rende conto che la relativa PageIndex proprietà è maggiore del numero totale di pagine nell'origine dati (dal momento che è stato eliminato l'ultimo record nell'ultima pagina) e quindi decrementa la relativa PageIndex proprietà. Nel passaggio 4 gridView tenta di associarsi ai dati recuperati nel passaggio 2; Tuttavia, nel passaggio 2 non sono stati restituiti record, pertanto viene restituito un gridView vuoto. Con il paging predefinito, questo problema non viene visualizzato perché nel passaggio 2 tutti i record vengono recuperati dall'origine dati.

Per risolvere questo problema sono disponibili due opzioni. Il primo consiste nel creare un gestore eventi per il gestore eventi gridView s RowDeleted che determina il numero di record visualizzati nella pagina appena eliminata. Se c'era un solo record, il record appena eliminato deve essere stato l'ultimo e dobbiamo decrerere il gridView s PageIndex. Naturalmente, si vuole aggiornare PageIndex solo se l'operazione di eliminazione ha avuto esito positivo, che può essere determinata assicurando che la e.Exception proprietà sia null.

Questo approccio funziona perché aggiorna il passaggio 1 ma prima del PageIndex passaggio 2. Pertanto, nel passaggio 2 viene restituito il set appropriato di record. A tale scopo, usare il codice simile al seguente:

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

Una soluzione alternativa consiste nel creare un gestore eventi per l'evento ObjectDataSource e RowDeleted impostare la AffectedRows proprietà su un valore pari a 1. Dopo aver eliminato il record nel passaggio 1 (ma prima di recuperare nuovamente i dati nel passaggio 2), GridView aggiorna la proprietà PageIndex se una o più righe sono state interessate dall'operazione. Tuttavia, la AffectedRows proprietà non è impostata da ObjectDataSource e pertanto questo passaggio viene omesso. Un modo per eseguire questo passaggio consiste nel impostare manualmente la AffectedRows proprietà se l'operazione di eliminazione viene completata correttamente. Questa operazione può essere eseguita usando il codice simile al seguente:

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

Il codice per entrambi questi gestori eventi è disponibile nella classe code-behind dell'esempio EfficientPaging.aspx .

Confronto delle prestazioni del paging predefinito e personalizzato

Poiché il paging personalizzato recupera solo i record necessari, mentre il paging predefinito restituisce tutti i record per ogni pagina visualizzata, è chiaro che il paging personalizzato è più efficiente rispetto al paging predefinito. Ma quanto è più efficiente è il paging personalizzato? Quale tipo di miglioramenti delle prestazioni può essere visto passando dal paging predefinito al paging personalizzato?

Sfortunatamente, non c'è una dimensione adatta a tutte le risposte qui. Il miglioramento delle prestazioni dipende da diversi fattori, i due più importanti sono il numero di record da visualizzare e il carico inserito nel server di database e nei canali di comunicazione tra il server Web e il server di database. Per le tabelle di piccole dimensioni con solo poche decine di record, la differenza delle prestazioni potrebbe essere trascurabile. Per tabelle di grandi dimensioni, con migliaia di migliaia di righe, anche se la differenza di prestazioni è acuta.

Un articolo della mia, "Paging personalizzato in ASP.NET 2.0 con SQL Server 2005", contiene alcuni test delle prestazioni eseguiti per mostrare le differenze di prestazioni tra queste due tecniche di paging durante il paging tramite una tabella di database con 50.000 record. In questi test sono stati esaminati sia il tempo per eseguire la query a livello di SQL Server (usando SQL Profiler) sia nella pagina ASP.NET usando le funzionalità di traccia di ASP.NET. Tenere presente che questi test sono stati eseguiti nella casella di sviluppo con un singolo utente attivo e quindi non sono scientifiche e non simulano i modelli di caricamento tipici del sito Web. Indipendentemente dal fatto che i risultati illustrano le differenze relative nel tempo di esecuzione per il paging predefinito e personalizzato quando si utilizzano quantità di dati sufficientemente grandi.

Durata media (sec) Reads
Paging SQL Profiler predefinito 1.411 383
Paging SQL Profiler personalizzato 0.002 29
Paging predefinito ASP.NET traccia 2.379 N/D
Paging personalizzato ASP.NET traccia 0.029 N/D

Come si può notare, recuperare una determinata pagina di dati necessari 354 letture in media e completate in una frazione del tempo. Nella pagina ASP.NET, la pagina personalizzata è stata in grado di eseguire il rendering in prossimitàdel 1 /100 1/100 del tempo necessario quando si usa il paging predefinito.

Riepilogo

Il paging predefinito è un cinch per implementare solo la casella di controllo Abilita paging nello smart tag del controllo Web dei dati, ma tale semplicità è a costo delle prestazioni. Con il paging predefinito, quando un utente richiede qualsiasi pagina di dati, tutti i record vengono restituiti, anche se è possibile visualizzare solo una piccola frazione di essi. Per combattere questo sovraccarico delle prestazioni, ObjectDataSource offre un'opzione di paging alternativa personalizzata.

Mentre il paging personalizzato migliora i problemi di prestazioni del paging predefinito recuperando solo i record che devono essere visualizzati, è più coinvolto per implementare il paging personalizzato. Prima di tutto, una query deve essere scritta in modo corretto (ed efficiente) accede al subset specifico dei record richiesti. Questa operazione può essere eseguita in diversi modi; quello esaminato in questa esercitazione consiste nell'usare la nuova ROW_NUMBER() funzione SQL Server 2005 per classificare i risultati e quindi restituire solo i risultati la cui classificazione rientra all'interno di un intervallo specificato. Inoltre, è necessario aggiungere un mezzo per determinare il numero totale di record da scorrere. Dopo aver creato questi metodi DAL e BLL, è anche necessario configurare ObjectDataSource in modo che possa determinare il numero di record totali visualizzati tramite e può passare correttamente i valori Di indice di riga iniziale e Righe massime al BLL.

Anche se l'implementazione di paging personalizzati richiede diversi passaggi e non è quasi semplice come il paging predefinito, il paging personalizzato è una necessità quando si esegue il paging in quantità sufficientemente elevata di dati. Come illustrato dai risultati esaminati, il paging personalizzato può perdere secondi dal tempo di rendering della pagina ASP.NET e può illuminare il carico nel server di database in base a un numero di ore più ordini di grandezza.

Programmazione felice!

Informazioni sull'autore

Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, lavora con le tecnologie Web Microsoft dal 1998. Scott lavora come consulente indipendente, formatore e scrittore. Il suo ultimo libro è Sams Teach Yourself ASP.NET 2.0 in 24 ore. Può essere raggiunto all'indirizzo mitchell@4GuysFromRolla.com. o tramite il suo blog, disponibile all'indirizzo http://ScottOnWriting.NET.