Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
L'opzione di paging predefinita di un controllo presentazione dati non è adatta quando si utilizzano grandi quantità di dati, poiché 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 sottoinsieme.
- 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, è sufficiente selezionare una casella di controllo e il gioco è fatto! La paginazione predefinita è un'opzione interessante. Il suo approccio semplicistico nel recupero di tutti i record, tuttavia, lo rende una scelta implausibile quando si gestiscono grandi quantità di dati o per siti con molti utenti concorrenti. In tali circostanze, è necessario rivolgersi 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 sottoinsieme appropriato di record. In questa esercitazione verrà illustrato come usare questa nuova parola chiave di 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 a quella successiva tramite il paging personalizzato può essere più veloce 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: Comprendere il 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:
- Indice di Inizio Riga l'indice della prima riga nella pagina di dati da visualizzare; questo indice può essere calcolato moltiplicando l'indice della 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 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 numero massimo di righe poiché per l'ultima pagina potrebbero essere restituiti meno record rispetto alle dimensioni della pagina. Ad esempio, quando si esegue il paging tra i 81 prodotti 10 record per pagina, la nona e la pagina finale avranno un solo record. Nessuna pagina mostrerà però più record rispetto al valore delle righe massime.
- 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 la dimensione massima delle righe è semplicemente la dimensione della pagina. Poiché il paging predefinito recupera tutti i record dal database quando si renderizza qualsiasi pagina di dati, l'indice di ogni riga è noto, rendendo così l'operazione di spostamento all'indice della riga iniziale un compito banale. Inoltre, il conteggio totale record è facilmente disponibile, poiché è semplicemente il numero di record in DataTable (o qualsiasi oggetto utilizzato per contenere i risultati del database).
In base alle variabili Start Row Index e Maximum Rows, un'implementazione di paging personalizzata deve restituire solo il sottoinsieme preciso di record a partire dall'indice di riga iniziale e fino al numero massimo di righe di record dopo di esso. 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 i 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 i metodi in DAL e BLL.
Passaggio 2: Restituzione del numero totale di record in fase di paginazione
Prima di esaminare come recuperare il sottoinsieme preciso di record per la pagina visualizzata, esaminiamo prima come restituire il numero totale di record paginati. 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
Aggiungiamo un metodo al nostro 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 su in Progettazione e scegliere Aggiungi query. Come illustrato nelle esercitazioni precedenti, questo consentirà di aggiungere un nuovo metodo al dal che, quando richiamato, eseguirà una particolare istruzione SQL o stored procedure. Come per i metodi del TableAdapter nelle esercitazioni precedenti, per questo si opta di utilizzare 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.
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.
Figura 3: Utilizza la query SELECT COUNT(*) FROM Products
Infine, specificare il nome per il metodo . Come accennato in precedenza, è possibile usare TotalNumberOfProducts.
Figura 4: Assegnare un nome al metodo DAL TotalNumberOfProducts
Dopo aver fatto clic su Fine, la procedura guidata aggiungerà il metodo TotalNumberOfProducts alla DAL. I metodi scalari nel DAL restituiscono tipi nullable, nel caso in cui il risultato della query SQL sia NULL. La COUNT query restituirà tuttavia sempre un valore diverso da un valore NULL; indipendentemente da ciò, il metodo DAL restituisce un numero intero annullabile.
Oltre al metodo DAL, è necessario anche un metodo nel BLL. Aprire il file della classe ProductsBLL e aggiungere un metodo TotalNumberOfProducts che chiama semplicemente il metodo TotalNumberOfProducts della DAL.
Public Function TotalNumberOfProducts() As Integer
Return Adapter.TotalNumberOfProducts().GetValueOrDefault()
End Function
Il metodo TotalNumberOfProducts del DAL restituisce un intero nullable; tuttavia, abbiamo creato il metodo della classe ProductsBLL in modo che restituisca un numero intero standard. È pertanto 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 dei 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 lo script SQL necessario. La sfida che ci si presenta è che dobbiamo essere in grado di assegnare in modo efficiente un indice a ogni riga dei 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 nella tabella di database è già presente una colonna che funge da indice di riga. A prima vista, potremmo pensare che il campo Products della tabella ProductID sia sufficiente, dato che il primo prodotto ha ProductID 1, il secondo 2, e così via. Tuttavia, l'eliminazione di un prodotto lascia un vuoto nella sequenza, nullizzando questo approccio.
Esistono due tecniche generali usate per associare in modo efficiente un indice di riga ai dati per sfogliare, consentendo di recuperare esattamente il sottoinsieme di record desiderato.
Uso della parola chiave di SQL Server 2005 Introduzione alla versione di SQL Server 2005, la 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.
Usando una variabile di tabella e
SET ROWCOUNTSET ROWCOUNTistruzione SQL Server si può specificare il numero di record totali che una query deve elaborare prima di terminare; le variabili di tabella sono variabili locali T-SQL che possono contenere dati tabulari, simili alle tabelle temporanee. Questo approccio funziona altrettanto bene con Microsoft SQL Server 2005 e SQL Server 2000 (mentre l'approccioROW_NUMBER()funziona solo con SQL Server 2005).L'idea è creare una variabile di tabella con una colonna
IDENTITYe colonne con le chiavi primarie della tabella di cui si paginano i dati. Successivamente, il contenuto della tabella i cui dati vengono paginati viene scaricato nella variabile tabella, associando quindi un indice di riga sequenziale (tramite la colonnaIDENTITY) per ogni record della tabella. Dopo aver popolato la variabile di tabella, è possibile eseguire un'istruzioneSELECTnella variabile di tabella unita alla tabella sottostante per estrarre i record specifici. L'istruzioneSET ROWCOUNTviene utilizzata per limitare in modo intelligente il numero di record da inserire nella variabile della tabella.L'efficienza di questo approccio si basa sul numero di pagina richiesto, perché al
SET ROWCOUNTvalore 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 tipiche del paging predefinito quando si recupera una pagina vicino alla fine.
Questa esercitazione implementa il paging personalizzato usando la ROW_NUMBER() keyword. Per ulteriori informazioni sull'uso della variabile di tabella e della tecnica SET ROWCOUNT, vedere Un metodo più efficiente per lo scorrimento di grandi set di risultati.
La parola chiave ROW_NUMBER() ha associato una classificazione a ogni record restituito in 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 per quanto riguarda l'ordinamento indicato. Ad esempio, per visualizzare il rango per ogni prodotto, ordinato 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 durante l'esecuzione nella 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.
Figura 5: La classificazione dei prezzi è inclusa per ogni record restituito
Nota
ROW_NUMBER() è solo una delle molte nuove funzioni di classificazione disponibili in SQL Server 2005. Per una descrizione più approfondita di ROW_NUMBER(), insieme alle altre funzioni di classificazione, leggere la documentazione di ROW_NUMBER.
Quando si classificano i risultati in base alla colonna specificata nella ORDER BY clausola (OVERnell'esempio precedenteUnitPrice), SQL Server deve ordinare i risultati. Si tratta di un'operazione rapida se è presente un indice cluster sulle colonne in cui vengono ordinati i risultati oppure se è presente un indice di copertura, ma può essere più costoso in caso contrario. Per migliorare le prestazioni nelle query di dimensioni sufficientemente grandi, è consigliabile aggiungere un indice non-cluster per la colonna in base alla quale i risultati vengono ordinati. Per informazioni più dettagliate sulle considerazioni sulle prestazioni, vedere Funzioni di classificazione e prestazioni in SQL Server 2005 .
Le informazioni di 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 è rigorosamente maggiore StartRowIndex di e minore o uguale a StartRowIndex + MaximumRows.
Ora che abbiamo discusso di come ROW_NUMBER() possa essere utilizzato per recuperare una determinata pagina di dati in base ai valori di Start Row Index e Maximum Rows, dobbiamo ora implementare questa logica come metodi nella Logica di Accesso ai Dati (DAL) e nella Logica di Business (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 un paging personalizzato in questo tutorial, non saremo in grado di creare un report paginato personalizzato che possa anche essere ordinato. 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 gradisce la sintassi OVER usata dalla funzione ROW_NUMBER(). 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).
Figura 6: Aggiungere una nuova stored procedure per la paginazione dei prodotti
Questa stored procedure deve accettare due parametri integer di input - @startRowIndex e @maximumRows - e usare la funzione ROW_NUMBER() ordinata dal campo ProductName, restituendo solo le righe maggiori di @startRowIndex e minori o uguali a @startRowIndex + @maximumRow. 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, è necessario attendere qualche minuto per testarla. 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 di immettere i parametri di input, @startRowIndex, e @maximumRow (vedere la figura 7). Provare valori diversi ed esaminare i risultati.
@startRowIndex e i @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 il valore 10 per entrambi i parametri @startRowIndex e @maximumRows.
Figura 8: Vengono restituiti i record che verrebbero visualizzati nella seconda pagina di dati (fare clic per visualizzare l'immagine a dimensione intera)
Dopo aver creato questa stored procedure, siamo pronti a creare il ProductsTableAdapter metodo. Aprire il Northwind.xsd DataSet tipizzato, fare clic con il pulsante destro del mouse su ProductsTableAdapter e scegliere l'opzione Aggiungi query. Anziché creare la query utilizzando un'istruzione SQL ad hoc, creala utilizzando 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.
Figura 10: Scegliere la Stored Procedure GetProductsPaged dal menu a tendina
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.
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.
Figura 12: Assegnare un nome ai 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 devono restituire solo i record che rientrano nell'intervallo specificato. Creare un metodo BLL di questo tipo nella classe ProductsBLL che si limita a chiamare il metodo del DAL GetProductsPaged, 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 ci risparmia un po' di lavoro extra durante la configurazione di ObjectDataSource per utilizzare 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 completato, è possibile creare un controllo GridView che scorre i record sottostanti usando 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 abbiamo spesso configurato l'ObjectDataSource per utilizzare il metodo della classe ProductsBLLGetProducts. 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.
Figura 13: Configurare ObjectDataSource per l'uso del metodo GetProductsPaged della classe ProductsBLL
Dato che si sta creando un GridView di sola lettura, impostare il menu a tendina del metodo nelle schede INSERT, UPDATE e DELETE su (Nessuno).
La procedura guidata ObjectDataSource richiede quindi le origini dei valori dei parametri di input del metodo GetProductsPaged, startRowIndex e maximumRows. Questi parametri di input verranno effettivamente impostati automaticamente da GridView, quindi è sufficiente lasciare l'origine impostata su Nessuno e fare clic su Fine.
Figura 14: Lasciare le origini dei parametri di input come Nessuna
Dopo aver completato la procedura guidata ObjectDataSource, il GridView conterrà un BoundField o un CheckBoxField per ognuno dei campi dati del prodotto. È possibile personalizzare l'aspetto di GridView nel modo desiderato. Ho scelto di visualizzare solo i ProductName, CategoryName, SupplierName, QuantityPerUnit e 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, il GridView non si trova da nessuna parte.
Figura 15: Il 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 visualizzato.
Per risolvere questo problema, è necessario configurare ObjectDataSource per l'uso del paging personalizzato. Questa operazione può essere eseguita nei passaggi seguenti:
- Imposta la proprietà di ObjectDataSource su
true; ciò indica che ObjectDataSource deve passare aSelectMethoddue parametri aggiuntivi: uno per specificare l'indice di riga iniziale (StartRowIndexParameterName) e uno per specificare le righe massime (MaximumRowsParameterName). - Imposta le proprietà
StartRowIndexParameterNameeMaximumRowsParameterNamedi ObjectDataSource di conseguenza, e le proprietàStartRowIndexParameterNameeMaximumRowsParameterNameindicano i nomi dei parametri di input passati aSelectMethodper scopi di paging personalizzati. Per impostazione predefinita, questi nomi di parametro sonostartIndexRowemaximumRows, motivo per cui, quando si crea ilGetProductsPagedmetodo 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, comeGetProductsPaged,startIndexedmaxRows, è necessario impostare di conseguenza le proprietàStartRowIndexParameterNameeMaximumRowsParameterNamedi ObjectDataSource, ad esempio startIndex perStartRowIndexParameterNamee maxRows perMaximumRowsParameterName. -
Impostare la proprietà del ObjectDataSource
SelectCountMethodsul nome del metodo che restituisce il numero totale di record visualizzati (TotalNumberOfProducts) ricordare che il metodo della classeProductsBLLTotalNumberOfProductsrestituisce il numero totale di record visualizzati tramite un metodo DAL che esegue una querySELECT COUNT(*) FROM Products. Queste informazioni sono necessarie per ObjectDataSource per eseguire correttamente il rendering dell'interfaccia di paging. -
Rimuovere gli elementi
startRowIndex,maximumRowse<asp:Parameter>dal markup dichiarativo dell'ObjectDataSource quando si configura l'ObjectDataSource tramite la procedura guidata; Visual Studio ha aggiunto automaticamente due elementi<asp:Parameter>per i parametri di input del metodoGetProductsPaged. ImpostandoEnablePagingsutrue, questi parametri verranno passati automaticamente. Se vengono visualizzati anche nella sintassi dichiarativa, ObjectDataSource tenterà di passare quattro parametri alGetProductsPagedmetodo e due parametri alTotalNumberOfProductsmetodo . 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.
Figura 16: Per usare il paging personalizzato, configurare il controllo ObjectDataSource
Dopo aver apportato queste modifiche, visitare questa pagina tramite un browser. Dovrebbero essere visualizzati 10 prodotti elencati, ordinati alfabeticamente. Prenditi un momento per esaminare i dati una pagina alla volta. Sebbene non ci sia alcuna differenza visiva dal punto di vista dell'utente finale tra il paging predefinito e il paging personalizzato, quest'ultimo effettua il paging in modo più efficiente su grandi quantità di dati, recuperando solo i record che devono essere visualizzati per una determinata pagina.
Figura 17: I dati, ordinati in base al nome del prodotto, vengono visualizzati tramite paging personalizzato (fare clic per visualizzare l'immagine a dimensione intera)
Nota
Con il paging personalizzato, il valore del conteggio delle pagine restituito da ObjectDataSource SelectCountMethod viene archiviato nello stato di visualizzazione di GridView. Altre variabili GridView, la raccolta PageIndex, EditIndex, SelectedIndex, DataKeys e così via, vengono archiviate nello stato di controllo, che viene mantenuto indipendentemente dal valore della proprietà GridViewEnableViewState. Poiché il valore PageCount viene salvato in modo permanente tra postback tramite il ViewState, quando si utilizza un'interfaccia di paging che include un collegamento per passare all'ultima pagina, è fondamentale che il ViewState 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 di pagina viene generato un postback e viene indicato a GridView di aggiornarne la PageIndex proprietà. Se si fa clic sull'ultimo collegamento di pagina, GridView assegna la relativa PageIndex proprietà a un valore minore della relativa PageCount proprietà. Con lo stato di visualizzazione disabilitato, il valore PageCount viene perso tra i postback e viene invece assegnato il valore intero massimo a PageIndex. GridView tenta quindi di determinare l'indice di riga iniziale moltiplicando le PageSize proprietà e PageCount . Ciò si traduce in un OverflowException poiché il prodotto supera la dimensione massima consentita per gli interi.
Implementare il paging e l'ordinamento personalizzati
L'implementazione attuale del paging personalizzato richiede che l'ordine in base al quale i dati vengono sottoposti a paging sia specificato in modo statico durante la creazione della stored procedure GetProductsPaged. Tuttavia, potresti aver notato che lo smart tag gridView contiene una casella di controllo Abilita ordinamento oltre all'opzione Abilita paging. Sfortunatamente, l'aggiunta del supporto per l'ordinamento a GridView con l'implementazione di paging personalizzata corrente consente di ordinare solo i record nella pagina di dati attualmente visualizzata. Ad esempio, se si configura GridView per supportare anche il paging e poi, quando si visualizza la prima pagina di dati, si ordina per nome del prodotto in ordine decrescente, capovolgerà l'ordine dei prodotti nella pagina 1. Come illustrato nella figura 18, questi mostrano Carnarvon Tigers come primo prodotto quando si ordina in ordine alfabetico inverso, che ignora i 71 altri prodotti che vengono dopo Carnarvon Tigers, alfabeticamente; solo i record nella prima pagina vengono considerati nell'ordinamento.
Figura 18: vengono ordinati solo i dati visualizzati nella pagina corrente (fare clic per visualizzare l'immagine a dimensione intera)
L'ordinamento si applica solo alla pagina di dati corrente perché l'ordinamento si verifica dopo che i dati sono stati recuperati dal metodo BLL e GetProductsPaged questo metodo restituisce solo i 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 di dati specifica. Verrà illustrato come eseguire questa operazione nell'esercitazione successiva.
Implementazione di funzionalità di paging ed eliminazione personalizzati
Se si abilita la funzionalità di eliminazione in un controllo GridView i cui dati vengono inseriti in pagine usando tecniche di paging personalizzate, si scopre che quando si elimina l'ultimo record dall'ultima pagina, GridView scompare invece di decrementare in modo appropriato gridView s PageIndex. Per riprodurre questo bug, abilita l'eliminazione per il tutorial che abbiamo appena creato. Vai all'ultima pagina (pagina 9), dove dovresti vedere un singolo prodotto, perché stiamo passando in rassegna 81 prodotti, 10 prodotti alla volta. Elimina 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 dallo schermo. Il motivo preciso per cui ciò accade è un po' fuori dall'ambito di questa esercitazione. Consultare Eliminazione dell'ultimo record nell'ultima pagina da un controllo GridView con paging personalizzato per i dettagli di basso livello in merito all'origine di questo problema. In sintesi, è dovuto alla sequenza di passaggi seguente che vengono eseguiti da GridView quando si fa clic sul pulsante Elimina:
- Eliminare il record
- Recuperare i record appropriati da visualizzare per i
PageIndexePageSizespecificati. - Assicurarsi che
PageIndexnon superi il numero di pagine di dati nella sorgente dati: in caso affermativo, decrementare automaticamente la proprietàPageIndexdel GridView - Associare la pagina di dati appropriata a GridView usando i record ottenuti nel passaggio 2
Il problema deriva dal fatto che nel passaggio 2 il PageIndex utilizzato per acquisire i record da visualizzare è ancora il PageIndex dell'ultima pagina il cui unico record è stato appena eliminato. Pertanto, nel passaggio 2 non sono restituiti record poiché l'ultima pagina di 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 (poiché è stato eliminato l'ultimo record nell'ultima pagina) e quindi ne decrementa PageIndex la proprietà. Nel passaggio 4 GridView tenta di associarsi ai dati recuperati nel passaggio 2; tuttavia, nel passaggio 2 non sono stati restituiti record, pertanto si ottiene un GridView vuoto. Con il paging predefinito, non si presenta questo problema 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 di RowDeleted GridView che determina il numero di record visualizzati nella pagina appena eliminata. Se è presente un solo record, il record appena eliminato deve essere stato l'ultimo ed è necessario decrementare gridView s PageIndex. Naturalmente, vogliamo aggiornare solo il PageIndex se l'operazione di eliminazione è stata effettivamente eseguita con successo, il che può essere determinato assicurandosi che la proprietà e.Exception sia null.
Questo approccio funziona perché aggiorna il PageIndex dopo il passaggio 1, ma prima del passaggio 2. Pertanto, nel passaggio 2 viene restituito il set appropriato di record. A tale scopo, usare 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 RowDeleted di ObjectDataSource e impostare la proprietà AffectedRows 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 PageIndex proprietà se una o più righe sono interessate dall'operazione. Tuttavia, la AffectedRows proprietà non è impostata da ObjectDataSource e pertanto questo passaggio viene omesso. Un modo per eseguire questo passaggio consiste nell'impostare manualmente la AffectedRows proprietà se l'operazione di eliminazione viene completata correttamente. Questa operazione può essere eseguita usando 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 miglioramento delle prestazioni può essere visto passando dal paging predefinito al paging personalizzato?
Sfortunatamente, non esiste una soluzione valida per tutti qui. Il miglioramento delle prestazioni dipende da diversi fattori, i due più importanti sono il numero di record sottoposti a paging e il carico sul server di database e sui 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 di prestazioni può essere trascurabile. Per le tabelle di grandi dimensioni, con migliaia a centinaia di migliaia di righe, tuttavia, la differenza di prestazioni è acuta.
Un articolo del mio articolo, "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 in una tabella di database con 50.000 record. In questi test sono stati esaminati sia il tempo necessario 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, quindi non sono scientifici e non simulano modelli di carico tipici del sito Web. Indipendentemente dai risultati, 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) | Legge | |
|---|---|---|
| Paginazione predefinita di SQL Profiler | 1.411 | 383 |
| Profiler SQL per la personalizzazione del paging | 0,002 | 29 |
| Paginazione predefinita ASP.NET Trace | 2.379 | N/D |
| Paging personalizzato ASP.NET traccia | 0.029 | N/D |
Come si può notare, il recupero di una determinata pagina di dati ha richiesto 354 letture in meno in media e che è stato completato in una frazione di tempo. Nella pagina ASP.NET, una pagina personalizzata è stata in grado di eseguire il rendering in quasi 1/100 del tempo necessario con il paging predefinito.
Riepilogo
Il paging predefinito è facile da implementare, è sufficiente selezionare la casella di controllo Abilita paging nello smart tag del controllo dati Web, ma questa semplicità incide sulle prestazioni. Con il paging predefinito, quando un utente richiede qualsiasi pagina di dati vengono restituiti tutti i record, anche se è possibile visualizzare solo una piccola frazione di essi. Per combattere questo sovraccarico delle prestazioni, ObjectDataSource offre un'opzione di paging alternativa personalizzata.
Anche se il paging personalizzato migliora i problemi di prestazioni del paging predefinito recuperando solo i record che devono essere visualizzati, è più necessario implementare il paging personalizzato. Prima di tutto, deve essere scritta una query che acceda in modo corretto (ed efficiente) al sottoinsieme specifico di record richiesti. Questa operazione può essere eseguita in diversi modi; quello esaminato in questa esercitazione consiste nell'usare la nuova ROW_NUMBER() funzione di SQL Server 2005 per classificare i risultati e quindi restituire solo i risultati la cui classificazione rientra in un intervallo specificato. Inoltre, è necessario aggiungere un metodo per determinare il numero totale di record in paginazione. Dopo aver creato questi metodi DAL e BLL, è anche necessario configurare ObjectDataSource in modo che possa determinare il numero di record totali di cui viene eseguito il paging e passare correttamente i valori Inizio indice riga e Righe massime al BLL.
Anche se l'implementazione di paging personalizzato richiede una serie di passaggi e non è quasi semplice come il paging predefinito, il paging personalizzato è una necessità quando si esegue il paging di grandi quantità di dati. Come illustrato dai risultati esaminati, il paging personalizzato può ridurre di secondi il tempo di rendering della pagina ASP.NET e può alleggerire il carico sul server di database di uno o più ordini di grandezza.
Buon programmatori!
Informazioni sull'autore
Scott Mitchell, autore di sette libri ASP/ASP.NET e fondatore di 4GuysFromRolla.com, ha lavorato 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 a mitchell@4GuysFromRolla.com.