Introduction
L'affichage d'un jeu important d'enregistrements dans une
application Active Server Pages (ASP) représente un problème
auquel vous avez souvent dû être confronté. Le
présent article analyse ce problème et propose une
solution et un exemple de code pouvant être facilement modifié
pour s'adapter à votre situation particulière. L'exemple
de code est conçu pour représenter une solution côté
serveur, indépendante du navigateur. J'aborderai au cours
de l'article des points qu'il est impératif de considérer
pour concevoir votre solution.
Problème
Votre requête a généré un jeu important
d'enregistrements. Vous devez trouver un moyen simple pour parcourir
les résultats en n'affichant qu'un sous-ensemble de résultats
par page. Pour pouvoir réaliser cette procédure efficacement,
il est impératif de bien comprendre comment collaborent ActiveX®
Data Objects (ADO) et votre base de données.
Solution
Comment partitionner votre jeu d'enregistrements en "pages"
plutôt que d'avoir un grand résultat unique ? Une page
se compose d'un nombre d'enregistrements spécifiés
pour s'afficher conjointement. Si votre jeu d'enregistrements se
compose par exemple de 100 enregistrements, vous pouvez visualiser
10 enregistrements par page.
ADO fournit deux méthodes, PageSize et AbsolutePage,
qui vous permettent de spécifier le nombre d'enregistrements
souhaités par page et de placer le curseur au début
d'une page.
Une fois votre jeu d'enregistrements ouvert, les principales étapes
sont alors les suivantes :
- Spécifiez un valeur PageSize pour le jeu d'enregistrements.
Elle correspond au nombre d'enregistrements à afficher par
page.
- Spécifiez le paramètre AbsolutePage pour
le jeu d'enregistrements. Cela permet de déplacer le pointeur
d'enregistrement au début d'une page donnée dans une
séquence de pages.
- Affichez la page d'enregistrements. Pour ce faire, itérez
votre jeu d'enregistrements un nombre de fois correspondant au paramètre
PageSize ou jusqu'à la fin du fichier.
Exemple de code
L'exemple de code suivant illustre la pagination. Vous pouvez l'utiliser
pour créer un prototype destiné à votre propre
solution. Dans votre code, assurez-vous que vous respectez bien
les points suivants :
- Ajoutez une gestion des erreurs.
- Limitez le nombre d'enregistrements renvoyés par votre
requête.
- Filtrez les enregistrements par des critères (créez
par exemple une clause WHERE).
- Utilisez une procédure stockée ou une vue.
Assurez-vous de bien modifier mon exemple de code pour pointer vers
votre base de données en modifiant la chaîne de connection
et l'instruction SQL. Le code utilisant des constantes ADO, telles
que adUserServer, vérifiez que vous faites référence
à ADO TypeLibrary dans votre fichier Global.asa, ou incluez
le fichier ADOVBS.INC dans votre page ASP. Remarque : Visual InterDev®
génère automatiquement la référence
TypeLibrary lorsque vous définissez une référence
du projet sur Microsoft ADO.
Notez que l'exemple fournit deux méthodes permettant de
créer une barre de navigation :
- ShowNavBar. Cette méthode permet aux utilisateurs
de passer à des pages spécifiques grâce au compteur
d'enregistrements. Elle utilise alors les propriétés
RecordCount et PageCount.
- ShowNavBarFast. Cette méthode ne permet pas de passer
à des pages spécifiques et ne fournit pas de compteur
d'enregistrement, mais vous permet de contrôler le nombre
d'enregistrements extraits par la propriété CacheSize.
PageThroughRs.Asp
<%@ Language=VBScript %>
<% Option Explicit %>
<SCRIPT LANGUAGE=VBScript RUNAT=SERVER>
'Assurez-vous de faire référence à ADO Typelib ou utilisez ADOVBS.Inc
Dim iPageNum, iRowsPerPage
Main
Sub Main()
Dim rst
Dim sSQL, sConnString
If Request.QueryString("iPageNum") = "" Then
iPageNum = 1
Else
iPageNum = Request.QueryString("iPageNum")
iPageNum = CInt(iPageNum)
End If
iRowsPerPage = 10
sConnString = "Provider=SQLOLEDB.1;password=Xyz123;user id=WebUser;" & _
"Initial Catalog=NorthWind;Data Source=MySQLServer;" & _
"network=dbmssocn;"
'L'instruction SQL suivante extrait toutes les colonnes d'une vue SQL.
'Pour optimiser la performance :
'- utilisez une procédure stockée, une vue, ou spécifiez des colonnes dans SELECT
'- utilisez des critères limitant les enregistrements renvoyés
'(clause WHERE par exemple)
sSQL = "SELECT CategoryName, ProductName, QuantityPerUnit,"
sSQL = sSQL & "UnitsInStock, Discontinued"
sSQL = sSQL & " FROM [Products By Category]"
Set rst = GetRecords(sConnString, sSQL)
WriteTableHeader rst
WriteTableBody rst, iRowsPerPage, iPageNum
ShowNavBar rst
'La méthode ShowFastNavBar n'utilise pas RecordCount
'ou PageCount, elle extrait uniquement le nombre d'enregistrements
' dicté par le paramètre CacheSize du jeu d'enregistrements
'ShowFastNavBar rst
CleanUp rst
End Sub
Function GetRecords(sConnString, sSQL)
Dim cnn
Dim rst
set cnn = Server.CreateObject("ADODB.connection")
cnn.connectionString = sConnString
cnn.Open
Set rst = Server.CreateObject("ADODB.RECORDSET")
Set rst.Activeconnection = cnn
'CursorLocation de adUseClient extrait
'tous les enregistrements lorsque le jeu d'enregistrements est ouvert.
'adUseServer permet d'optimiser le paramètre CacheSize
rst.CursorLocation = adUseServer
'CacheSize limite les lignes extraites en utilisant un
'curseur côté serveur. La manipulation porte uniquement
'sur le nombre d'enregistrements affichés - iRowsPerPage
rst.CacheSize = iRowsPerPage
rst.Open sSQL,,adOpenStatic, adLockReadOnly
Set GetRecords = rst
end Function
Sub WriteTableHeader(rst)
Dim fld
Response.Write "<TABLE WIDTH=80% BORDER=1>"
Response.Write "<TR>"
'Crée des titres de colonne pour le tableau
For Each fld in rst.Fields
Response.Write "<TD><B>" & fld.Name & "</B></TD>"
Next
Response.Write "</TR>"
End Sub
Sub WriteTableBody(rst, iRowsPerPage, iPageNum)
Dim iLoop
Dim fld
iLoop = 1
rst.PageSize = iRowsPerPage
rst.AbsolutePage = iPageNum
'Écrit la page d'enregistrements active
Do While (Not rst.EOF) and (iLoop <= iRowsPerPage)
Response.Write "<TR>"
For Each fld in rst.Fields
Response.Write "<TD>" & fld.value & "</TD>"
Next
iLoop = iLoop + 1
rst.MoveNext
Response.Write "</TR>"
Loop
Response.Write "</TABLE>"
End Sub
Sub ShowNavBar(rst)
Dim iPageCount
Dim iLoop
Dim sScriptName
'Cette version permet une navigation utilisateur plus riche mais
'repose sur RecordCount et PageCount, qui inversent
'les avantages de la spécification d'un paramètre CacheSize pour un
'curseur coté serveur.
Response.Write "<BR><BR>"
sScriptName = Request.ServerVariables("SCRIPT_NAME")
If iPageNum > 1 Then
Response.Write " <a class="n" href=" & sScriptName & "?iPageNum="
Response.Write (iPageNum -1) & "><< Previous</a>"
End If
iPageCount = rst.PageCount
Do Until iLoop > iPageCount
If iLoop = iPageNum Then
Response.Write " <B>" & CStr(iLoop) & "</B>"
Else
Response.Write " <a class="n" href=" & sScriptName & "?iPageNum=" & _
Cstr(iLoop) & ">" & iLoop & "</a>"
End If
iLoop = iLoop + 1
Loop
If Not rst.EOF Then
Response.Write " <a class="n" href=" & sScriptName & "?iPageNum="
Response.Write (iPageNum +1) & "> Next >></a><BR>"
Else
Response.Write "<BR>"
End If
Response.Write "Page " & iPageNum & " of " & iPageCount & "<BR>"
Response.Write rst.RecordCount & " Records"
End Sub
Sub ShowFastNavBar(rst)
Dim iPageCount
Dim iLoop
Dim sScriptName
'Cette méthode est très efficace en spécifiant un paramètre CacheSize
'et en utilisant un curseur côté serveur, car elle n'utilise ni
'RecordCount ni PageCount. Cela se fait au détriment
'de l'expérience de l'utilisateur.
Response.Write "<BR><BR>"
sScriptName = Request.ServerVariables("SCRIPT_NAME")
If iPageNum > 1 Then
Response.Write " <a class="n" href=" & sScriptName & "?iPageNum="
Response.Write (iPageNum -1) & "><< Previous</a>"
End If
If Not rst.EOF Then
Response.Write " <a class="n" href=" & sScriptName & "?iPageNum="
Response.Write (iPageNum +1) & "> Next >></a><BR>"
Else
Response.Write "<BR>"
End If
Response.Write "Page " & iPageNum
End Sub
Sub CleanUp(rst)
If Not rst Is Nothing then
If rst.state = adStateOpen then rst.close
set rst = nothing
End If
End Sub
</SCRIPT>
Analyse
Plusieurs points doivent être considérés lorsque
vous concevez une solution de pagination :
- Emplacement du curseur. Si vous utilisez un curseur côté
client, tous les enregistrements sont lus à chaque ouverture
du jeu d'enregistrements. En conséquence, étant donné
que tous les enregistrements sont lus, il est rapide d'accéder
ultérieurement à la propriété RecordCount
ou PageCount. Si vous utilisez un curseur côté
serveur, les enregistrements sont extraits lorsque vous en avez
besoin. Vous pouvez spécifier le nombre d'enregistrements
lus en une fois par la propriété CacheSize
pour améliorer les performances. Si vous utilisez cependant
les propriétés RecordCount ou PageCount
avec un curseur côté serveur, tous les enregistrements
sont lus, ce qui annule le gain de performances. Vous devez faire
la part des choses entre proposer une interface utilisateur offrant
plus d'informations et une navigation plus riche, ou réduire
la performance en extrayant tous les enregistrements.
- Lorsque vous utilisez un curseur côté serveur, la
propriété CursorType doit être adOpenStatic
ou adOpenKeyset pour utiliser la pagination.
- La pagination ne représente pas systématiquement
l'interface utilisateur appropriée. De bons scénarios
peuvent être des situations dans lesquelles les utilisateurs
analysent les résultats depuis un moteur de recherche ou
parcourent un catalogue de produits.
- Essayez de trier les enregistrements de façon à
ce que les enregistrements plus pertinents apparaissent dans les
premières pages (en utilisant par exemple la clause SQL ORDER
BY). Les utilisateurs peuvent alors gagner du temps.
- Extrayez uniquement les colonnes à afficher (évitez
par exemple SELECT *).
- Extrayez uniquement les enregistrements à afficher. Utilisez
des critères de filtrage (utilisez par exemple une clause
WHERE).
Vous trouverez ci-après quelques conseils supplémentaires
:
- Encapsulez vos valeurs logiques dans des méthodes. L'utilisation
de méthodes m'a permis de séparer la logique de présentation
de la logique d'accès aux données, ce qui simplifie
le portage du code en classes ou composants Windows Script Components
ou Visual Basic Scripting Edition (VBScript). Vous facilitez ainsi
toute modification des fonctionnalités et améliorez
la maintenance du code. Tests et débogages sont également
améliorés car les appels de méthode peuvent
être commentés et les commentaires annulés.
- Une référence TypeLibrary de ADO est préférable
à l'inclusion de ADOVBS.INC, car ASP lit tout le fichier
en mémoire lorsqu'il traire les inclusions, et non uniquement
ce dont il a besoin.
Conclusion
La pagination est une technique commune utilisée par
de nombreuses applications Web pour faciliter la navigation dans
un nombre d'enregistrements important. Lorsque vous créez
une solution de pagination, vous devez envisager un certain nombre
de points, tels la façon dont les enregistrements sont extraits
et le type de navigation utilisateur à offrir. Si la meilleure
solution dépend de facteurs propres à votre application,
le respect des techniques mentionnées dans cet article vous
aidera à prendre les meilleurs décisions en matière
de conception.
Initialement publié sur MSDN France le
25 avril 2002
|