Partager via


Pagination efficace dans de grandes quantités de données (C#)

par Scott Mitchell

Télécharger le PDF

L’option de pagination par défaut d’un contrôle de présentation de données n’est pas adaptée lors de l’utilisation de grandes quantités de données, car son contrôle de source de données sous-jacent récupère tous les enregistrements, même si seul un sous-ensemble de données est affiché. Dans de telles circonstances, nous devons nous tourner vers la pagination personnalisée.

Introduction

Comme nous l’avons vu dans le tutoriel précédent, la pagination peut être implémentée de deux façons :

  • La pagination par défaut peut être implémentée en vérifiant simplement l’option Activer la pagination dans la balise active du contrôle Web de données. Toutefois, chaque fois que vous affichez une page de données, ObjectDataSource récupère tous les enregistrements, même si seul un sous-ensemble d’entre eux est affiché dans la page
  • La pagination personnalisée améliore les performances de la pagination par défaut en récupérant uniquement les enregistrements de la base de données qui doivent être affichés pour la page particulière des données demandées par l’utilisateur ; toutefois, la pagination personnalisée implique un peu plus d’efforts pour implémenter que la pagination par défaut

En raison de la facilité d’implémentation, cochez simplement une case et vous avez terminé ! la pagination par défaut est une option attrayante. Son approche naïve de la récupération de tous les enregistrements, cependant, en fait un choix invraisemblable lors de la pagination de suffisamment grandes quantités de données ou pour les sites avec de nombreux utilisateurs simultanés. Dans de telles circonstances, nous devons nous tourner vers la pagination personnalisée afin de fournir un système réactif.

Le défi de la pagination personnalisée est de pouvoir écrire une requête qui retourne l’ensemble précis d’enregistrements nécessaires pour une page de données particulière. Heureusement, Microsoft SQL Server 2005 fournit un nouveau mot clé pour les résultats de classement, ce qui nous permet d’écrire une requête qui peut récupérer efficacement le sous-ensemble approprié d’enregistrements. Dans ce tutoriel, nous allons voir comment utiliser ce nouveau mot clé SQL Server 2005 pour implémenter la pagination personnalisée dans un contrôle GridView. Bien que l’interface utilisateur pour la pagination personnalisée soit identique à celle de la pagination par défaut, l’exécution pas à pas d’une page à l’autre à l’aide de la pagination personnalisée peut être plusieurs ordres de grandeur plus rapides que la pagination par défaut.

Remarque

Le gain de performances exact exposé par la pagination personnalisée dépend du nombre total d’enregistrements paginés et de la charge placée sur le serveur de base de données. À la fin de ce tutoriel, nous allons examiner certaines métriques approximatives qui présentent les avantages des performances obtenues via la pagination personnalisée.

Étape 1 : Présentation du processus de pagination personnalisé

Lors de la pagination des données, les enregistrements précis affichés dans une page dépendent de la page des données demandées et du nombre d’enregistrements affichés par page. Par exemple, imaginez que nous voulions parcourir les 81 produits, affichant 10 produits par page. Lors de l’affichage de la première page, nous voulons des produits 1 à 10 ; lors de l’affichage de la deuxième page, nous seriez intéressés par les produits 11 à 20, et ainsi de suite.

Il existe trois variables qui déterminent quels enregistrements doivent être récupérés et comment l’interface de pagination doit être rendue :

  • Démarrez l’index de ligne de la première ligne de la page de données à afficher ; cet index peut être calculé en multipliant l’index de page par les enregistrements à afficher par page et en en ajoutant un. Par exemple, lors de la pagination des enregistrements 10 à la fois, pour la première page (dont l’index de page est 0), l’index de ligne de démarrage est 0 * 10 + 1 ou 1 ; pour la deuxième page (dont l’index de page est 1), l’index de ligne de début est 1 * 10 + 1 ou 11.
  • Nombre maximal d’enregistrements à afficher par page. Cette variable est appelée lignes maximales, car pour la dernière page, il peut y avoir moins d’enregistrements retournés que la taille de la page. Par exemple, lors de la pagination des 81 produits 10 enregistrements par page, la neuvième et dernière page n’aura qu’un seul enregistrement. Aucune page n’affiche toutefois plus d’enregistrements que la valeur Nombre maximal de lignes.
  • Nombre total d’enregistrements le nombre total d’enregistrements paginés. Bien que cette variable ne soit pas nécessaire pour déterminer les enregistrements à récupérer pour une page donnée, elle détermine l’interface de pagination. Par exemple, si 81 produits sont paginés, l’interface de pagination sait afficher neuf numéros de page dans l’interface utilisateur de pagination.

Avec la pagination par défaut, l’index de ligne de démarrage est calculé en tant que produit de l’index de page et de la taille de page plus un, tandis que les lignes maximales sont simplement la taille de page. Étant donné que la pagination par défaut récupère tous les enregistrements de la base de données lors du rendu d’une page de données, l’index de chaque ligne est connu, ce qui rend le déplacement vers la ligne Démarrer l’index de ligne une tâche triviale. De plus, le nombre total d’enregistrements est facilement disponible, car il s’agit simplement du nombre d’enregistrements dans dataTable (ou de l’objet utilisé pour contenir les résultats de la base de données).

Étant donné les variables d’index de ligne de début et de ligne maximale, une implémentation de pagination personnalisée doit uniquement retourner le sous-ensemble précis d’enregistrements commençant à l’index de ligne de début et jusqu’au nombre maximal d’enregistrements après cela. La pagination personnalisée offre deux défis :

  • Nous devons pouvoir associer efficacement un index de ligne à chaque ligne de l’ensemble des données paginées afin que nous puissions commencer à retourner des enregistrements à l’index de ligne de début spécifié.
  • Nous devons fournir le nombre total d’enregistrements paginés via

Dans les deux étapes suivantes, nous allons examiner le script SQL nécessaire pour répondre à ces deux défis. En plus du script SQL, nous devons également implémenter des méthodes dans dal et BLL.

Étape 2 : renvoi du nombre total d’enregistrements paginés

Avant d’examiner comment récupérer le sous-ensemble précis d’enregistrements pour la page affichée, examinons d’abord comment retourner le nombre total d’enregistrements paginés. Ces informations sont nécessaires pour configurer correctement l’interface utilisateur de pagination. Le nombre total d’enregistrements retournés par une requête SQL particulière peut être obtenu à l’aide de la fonction d’agrégation.COUNT Par exemple, pour déterminer le nombre total d’enregistrements dans la Products table, nous pouvons utiliser la requête suivante :

SELECT COUNT(*)
FROM Products

Ajoutons une méthode à notre dal qui retourne ces informations. En particulier, nous allons créer une méthode DAL appelée TotalNumberOfProducts() qui exécute l’instruction SELECT indiquée ci-dessus.

Commencez par ouvrir le Northwind.xsd fichier Typed DataSet dans le App_Code/DAL dossier. Ensuite, cliquez avec le bouton droit sur le ProductsTableAdapter concepteur et choisissez Ajouter une requête. Comme nous l’avons vu dans les didacticiels précédents, cela nous permettra d’ajouter une nouvelle méthode à la dal qui, lorsqu’elle est appelée, exécute une instruction SQL ou une procédure stockée particulière. Comme avec nos méthodes TableAdapter dans les didacticiels précédents, pour celui-ci, optez pour utiliser une instruction SQL ad hoc.

Utiliser une instruction SQL ad hoc

Figure 1 : Utiliser une instruction SQL ad hoc

Dans l’écran suivant, nous pouvons spécifier le type de requête à créer. Étant donné que cette requête retourne une valeur scalaire unique, le nombre total d’enregistrements dans la Products table choisit l’option SELECT qui retourne une option de valeur singe.

Configurer la requête pour utiliser une instruction SELECT qui retourne une valeur unique

Figure 2 : Configurer la requête pour utiliser une instruction SELECT qui retourne une valeur unique

Après avoir indiqué le type de requête à utiliser, nous devons ensuite spécifier la requête.

Utiliser la requête SELECT COUNT(*) FROM Products

Figure 3 : Utiliser la requête SELECT COUNT(*) FROM Products

Enfin, spécifiez le nom de la méthode. Comme mentionné ci-dessus, nous allons utiliser TotalNumberOfProducts.

Nommez la méthode DAL TotalNumberOfProducts

Figure 4 : Nommer la méthode DAL TotalNumberOfProducts

Après avoir cliqué sur Terminer, l’Assistant ajoute la TotalNumberOfProducts méthode au dal. Les méthodes de retour scalaires dans les types nullables de retour DAL, au cas où le résultat de la requête SQL était NULL. Toutefois, notre COUNT requête retourne toujours une valeur non-valeurNULL ; quelle que soit la méthode DAL, elle renvoie un entier nullable.

En plus de la méthode DAL, nous avons également besoin d’une méthode dans la BLL. Ouvrez le ProductsBLL fichier de classe et ajoutez une TotalNumberOfProducts méthode qui appelle simplement la méthode dal s TotalNumberOfProducts :

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

La méthode DAL TotalNumberOfProducts renvoie un entier nullable . Toutefois, nous avons créé la ProductsBLL méthode de classe pour TotalNumberOfProducts qu’elle retourne un entier standard. Par conséquent, nous devons que la ProductsBLL méthode de TotalNumberOfProducts classe retourne la partie valeur de l’entier nullable retourné par la méthode DAL.TotalNumberOfProducts L’appel à renvoyer GetValueOrDefault() la valeur de l’entier nullable, s’il existe ; si l’entier nullable est null, toutefois, il retourne la valeur entière par défaut, 0.

Étape 3 : Retour du sous-ensemble précis d’enregistrements

Notre prochaine tâche consiste à créer des méthodes dans le dal et BLL qui acceptent les variables d’index de ligne de début et de ligne maximale décrites précédemment et retournent les enregistrements appropriés. Avant cela, examinons d’abord le script SQL nécessaire. Le défi auquel nous sommes confrontés est que nous devons être en mesure d’affecter efficacement un index à chaque ligne dans l’ensemble des résultats mis en page afin que nous puissions retourner uniquement ces enregistrements à partir de l’index de ligne de démarrage (et jusqu’au nombre maximal d’enregistrements).

Ce n’est pas un défi s’il existe déjà une colonne dans la table de base de données qui sert d’index de ligne. À première vue, nous pourrions penser que le Products champ de ProductID table suffirait, car le premier produit a ProductID 1, le deuxième a 2, et ainsi de suite. Toutefois, la suppression d’un produit laisse un écart dans la séquence, ce qui annule cette approche.

Il existe deux techniques générales utilisées pour associer efficacement un index de ligne aux données à la page, ce qui permet de récupérer le sous-ensemble précis d’enregistrements :

  • À l’aide du ROW_NUMBER() mot clé SQL Server 2005 nouveau dans SQL Server 2005, le ROW_NUMBER() mot clé associe un classement à chaque enregistrement retourné en fonction de certains classements. Ce classement peut être utilisé comme index de ligne pour chaque ligne.

  • L’utilisation d’une variable de table et SET ROWCOUNT de l’instruction SQL Server peut être utilisée pour spécifier le nombre total d’enregistrements qu’une requête doit traiter avant de SET ROWCOUNT terminer ; les variables de table sont des variables T-SQL locales qui peuvent contenir des données tabulaires, comme des tables temporaires. Cette approche fonctionne également bien avec Microsoft SQL Server 2005 et SQL Server 2000 (alors que l’approche ROW_NUMBER() fonctionne uniquement avec SQL Server 2005).

    L’idée ici est de créer une variable de table qui a une IDENTITY colonne et des colonnes pour les clés primaires de la table dont les données sont paginées. Ensuite, le contenu de la table dont les données sont paginées est vidé dans la variable de table, associant ainsi un index de ligne séquentiel (via la IDENTITY colonne) pour chaque enregistrement de la table. Une fois la variable de table remplie, une SELECT instruction sur la variable de table, jointe à la table sous-jacente, peut être exécutée pour extraire les enregistrements particuliers. L’instruction SET ROWCOUNT est utilisée pour limiter intelligemment le nombre d’enregistrements qui doivent être vidés dans la variable de table.

    L’efficacité de cette approche est basée sur le numéro de page demandé, car la SET ROWCOUNT valeur est affectée à la valeur de l’index de ligne de début plus les lignes maximales. Lorsque vous paginez des pages à faible nombre, telles que les premières pages de données, cette approche est très efficace. Toutefois, il présente les performances de pagination par défaut lors de la récupération d’une page près de la fin.

Ce didacticiel implémente la pagination personnalisée à l’aide du ROW_NUMBER() mot clé. Pour plus d’informations sur l’utilisation de la variable de table et SET ROWCOUNT de la technique, consultez Pagination efficace via de grandes quantités de données.

Le ROW_NUMBER() mot clé associé à un classement avec chaque enregistrement retourné sur un classement particulier à l’aide de la syntaxe suivante :

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

ROW_NUMBER() retourne une valeur numérique qui spécifie le classement de chaque enregistrement en ce qui concerne l’ordre indiqué. Par exemple, pour voir le classement de chaque produit, classé du plus cher au moins, nous pourrions utiliser la requête suivante :

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

La figure 5 montre les résultats de cette requête lors de l’exécution dans la fenêtre de requête dans Visual Studio. Notez que les produits sont commandés par prix, ainsi qu’un classement des prix pour chaque ligne.

Le classement des prix est inclus pour chaque enregistrement retourné

Figure 5 : Le classement des prix est inclus pour chaque enregistrement retourné

Remarque

ROW_NUMBER() n’est qu’une des nombreuses nouvelles fonctions de classement disponibles dans SQL Server 2005. Pour une discussion plus approfondie sur , ainsi que les autres fonctions de ROW_NUMBER()classement, lisez Retour des résultats classés avec Microsoft SQL Server 2005.

Lors du classement des résultats par la colonne spécifiée ORDER BY dans la OVER clause (UnitPricedans l’exemple ci-dessus), SQL Server doit trier les résultats. Il s’agit d’une opération rapide s’il existe un index cluster sur les colonnes par laquelle les résultats sont classés, ou s’il existe un index de couverture, mais peut être plus coûteux sinon. Pour améliorer les performances des requêtes suffisamment volumineuses, envisagez d’ajouter un index non cluster pour la colonne par laquelle les résultats sont classés. Consultez fonctions de classement et performances dans SQL Server 2005 pour obtenir un aperçu plus détaillé des considérations relatives aux performances.

Les informations de classement retournées par ROW_NUMBER() ne peuvent pas être utilisées directement dans la WHERE clause. Toutefois, une table dérivée peut être utilisée pour retourner le ROW_NUMBER() résultat, qui peut ensuite apparaître dans la WHERE clause. Par exemple, la requête suivante utilise une table dérivée pour retourner les colonnes ProductName et UnitPrice, ainsi que le ROW_NUMBER() résultat, puis utilise une WHERE clause pour retourner uniquement les produits dont le classement des prix est compris entre 11 et 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

En étendant ce concept un peu plus loin, nous pouvons utiliser cette approche pour récupérer une page spécifique de données en fonction des valeurs d’index de ligne de démarrage souhaitées et valeurs maximales de lignes :

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

Remarque

Comme nous le verrons plus loin dans ce tutoriel, l’indexé StartRowIndex fourni par ObjectDataSource commence à zéro, tandis que la ROW_NUMBER() valeur retournée par SQL Server 2005 est indexée à partir de 1. Par conséquent, la WHERE clause retourne ces enregistrements PriceRank qui sont strictement supérieurs StartRowIndex et inférieurs ou égaux à StartRowIndex + MaximumRows.

Maintenant que nous avons abordé comment ROW_NUMBER() récupérer une page particulière de données en fonction des valeurs d’index de ligne de démarrage et de nombre maximal de lignes, nous devons maintenant implémenter cette logique en tant que méthodes dans la dal et BLL.

Lors de la création de cette requête, nous devons décider de l’ordre par lequel les résultats seront classés ; trions les produits par leur nom par ordre alphabétique. Cela signifie qu’avec l’implémentation de pagination personnalisée dans ce tutoriel, nous ne pouvons pas créer un rapport paginé personnalisé que vous pouvez également trier. Dans le tutoriel suivant, cependant, nous verrons comment ces fonctionnalités peuvent être fournies.

Dans la section précédente, nous avons créé la méthode DAL en tant qu’instruction SQL ad hoc. Malheureusement, l’analyseur T-SQL dans Visual Studio utilisé par l’Assistant TableAdapter n’aime pas la OVER syntaxe utilisée par la ROW_NUMBER() fonction. Par conséquent, nous devons créer cette méthode DAL en tant que procédure stockée. Sélectionnez l’Explorateur de serveurs dans le menu Affichage (ou appuyez sur Ctrl+Alt+S) et développez le NORTHWND.MDF nœud. Pour ajouter une nouvelle procédure stockée, cliquez avec le bouton droit sur le nœud Procédures stockées et choisissez Ajouter une nouvelle procédure stockée (voir la figure 6).

Ajouter une nouvelle procédure stockée pour la pagination par le biais des produits

Figure 6 : Ajouter une nouvelle procédure stockée pour la pagination des produits

Cette procédure stockée doit accepter deux paramètres d’entrée entiers @startRowIndex et @maximumRows utiliser la ROW_NUMBER() fonction ordonnée par le ProductName champ, en retournant uniquement ces lignes supérieures à celles spécifiées @startRowIndex et inférieures ou égales à @startRowIndex + @maximumRow s. Entrez le script suivant dans la nouvelle procédure stockée, puis cliquez sur l’icône Enregistrer pour ajouter la procédure stockée à la base de données.

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)

Après avoir créé la procédure stockée, prenez un moment pour le tester. Cliquez avec le bouton droit sur le nom de la GetProductsPaged procédure stockée dans l’Explorateur de serveurs et choisissez l’option Exécuter. Visual Studio vous invite ensuite à entrer les paramètres d’entrée et @startRowIndex @maximumRow s (voir la figure 7). Essayez différentes valeurs et examinez les résultats.

Entrez une valeur pour la classe <span=@startRowIndex et @maximumRows paramètres » />

Figure 7 : Entrer une valeur pour les paramètres et @maximumRows les @startRowIndex paramètres

Après avoir choisi ces valeurs de paramètres d’entrée, la fenêtre Sortie affiche les résultats. La figure 8 montre les résultats lors de la transmission de 10 pour les paramètres et @maximumRows les @startRowIndex paramètres.

Les enregistrements qui s’affichent dans la deuxième page de données sont retournés

Figure 8 : Les enregistrements qui s’affichent dans la deuxième page de données sont retournés (cliquez pour afficher l’image de taille complète)

Avec cette procédure stockée créée, nous sommes prêts à créer la ProductsTableAdapter méthode. Ouvrez l’ensemble de données typé, cliquez avec le Northwind.xsd bouton droit dans le ProductsTableAdapterfichier , puis choisissez l’option Ajouter une requête. Au lieu de créer la requête à l’aide d’une instruction SQL ad hoc, créez-la à l’aide d’une procédure stockée existante.

Créer la méthode DAL à l’aide d’une procédure stockée existante

Figure 9 : Créer la méthode DAL à l’aide d’une procédure stockée existante

Ensuite, nous sommes invités à sélectionner la procédure stockée à appeler. Sélectionnez la GetProductsPaged procédure stockée dans la liste déroulante.

Choisissez la procédure stockée GetProductsPaged dans la liste déroulante

Figure 10 : Choisir la procédure stockée GetProductsPaged dans la liste déroulante

L’écran suivant vous demande ensuite quel type de données est retourné par la procédure stockée : données tabulaires, valeur unique ou aucune valeur. Étant donné que la GetProductsPaged procédure stockée peut retourner plusieurs enregistrements, indiquez qu’elle retourne des données tabulaires.

Indiquer que la procédure stockée retourne des données tabulaires

Figure 11 : Indiquer que la procédure stockée retourne des données tabulaires

Enfin, indiquez les noms des méthodes que vous souhaitez créer. Comme avec nos didacticiels précédents, poursuivez et créez des méthodes à l’aide du remplissage d’un DataTable et d’un DataTable. Nommez la première méthode FillPaged et la seconde GetProductsPaged.

Nommez les méthodes FillPaged et GetProductsPaged

Figure 12 : Nommer les méthodes FillPaged et GetProductsPaged

En plus de créer une méthode DAL pour retourner une page particulière de produits, nous devons également fournir ces fonctionnalités dans la BLL. Comme la méthode DAL, la méthode GetProductsPaged de BLL doit accepter deux entrées entières pour spécifier l’index de ligne de début et les lignes maximales, et doit retourner uniquement ces enregistrements qui se trouvent dans la plage spécifiée. Créez une telle méthode BLL dans la classe ProductsBLL qui appelle simplement dans la méthode GetProductsPaged de DAL, comme suit :

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

Vous pouvez utiliser n’importe quel nom pour les paramètres d’entrée de la méthode BLL, mais, comme nous le verrons bientôt, choisissez d’utiliser startRowIndex et maximumRows enregistrez-nous à partir d’un peu de travail lors de la configuration d’un ObjectDataSource pour utiliser cette méthode.

Étape 4 : Configuration de ObjectDataSource pour utiliser la pagination personnalisée

Avec les méthodes BLL et DAL pour accéder à un sous-ensemble particulier d’enregistrements terminés, nous sommes prêts à créer un contrôle GridView qui pages par le biais de ses enregistrements sous-jacents à l’aide de la pagination personnalisée. Commencez par ouvrir la EfficientPaging.aspx page dans le PagingAndSorting dossier, ajoutez un GridView à la page et configurez-le pour utiliser un nouveau contrôle ObjectDataSource. Dans nos didacticiels passés, nous avons souvent configuré ObjectDataSource pour utiliser la ProductsBLL méthode de GetProducts classe. Cette fois,cependant, nous voulons utiliser la méthode à la GetProductsPaged place, puisque la GetProducts méthode retourne tous les produits de la base de données, tandis que GetProductsPaged retourne uniquement un sous-ensemble particulier d’enregistrements.

Configurer ObjectDataSource pour utiliser la méthode GetProductsPaged de la classe ProductsBLL

Figure 13 : Configurer ObjectDataSource pour utiliser la méthode GetProductsPaged de la classe ProductsBLL

Étant donné que nous créons un GridView en lecture seule, prenez un moment pour définir la liste déroulante de méthode dans les onglets INSERT, UPDATE et DELETE sur (Aucun).

Ensuite, l’Assistant ObjectDataSource nous invite à entrer les sources des valeurs des paramètres de GetProductsPaged startRowIndex méthode et maximumRows d’entrée. Ces paramètres d’entrée sont définis automatiquement par GridView. Il vous suffit donc de laisser la source définie sur None, puis de cliquer sur Terminer.

Laissez les sources de paramètres d’entrée comme aucune

Figure 14 : Laisser les sources de paramètres d’entrée comme aucune

Une fois l’Assistant ObjectDataSource terminé, GridView contient un Objet BoundField ou CheckBoxField pour chacun des champs de données de produit. N’hésitez pas à personnaliser l’apparence de GridView comme vous le voyez. J’ai choisi d’afficher uniquement les ProductNamechamps , , QuantityPerUnitCategoryNameSupplierNameet UnitPrice BoundFields. Configurez également GridView pour prendre en charge la pagination en cochant la case Activer la pagination dans sa balise active. Après ces modifications, le balisage déclaratif GridView et ObjectDataSource doit ressembler à ce qui suit :

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

Si vous visitez la page via un navigateur, toutefois, gridView n’est pas là où trouver.

GridView n’est pas affiché

Figure 15 : Le GridView n’est pas affiché

GridView est manquant, car ObjectDataSource utilise actuellement 0 comme valeurs pour les paramètres d’entrée et maximumRows les GetProductsPaged startRowIndex paramètres d’entrée. Par conséquent, la requête SQL résultante ne retourne aucun enregistrement et, par conséquent, gridView n’est pas affiché.

Pour résoudre ce problème, nous devons configurer ObjectDataSource pour utiliser la pagination personnalisée. Pour ce faire, procédez comme suit :

  1. Définissez la propriété trueObjectDataSource EnablePaging sur ceci indique à ObjectDataSource qu’il doit passer aux SelectMethod deux paramètres supplémentaires : un pour spécifier l’index de ligne de début (StartRowIndexParameterName) et l’autre pour spécifier les lignes maximales (MaximumRowsParameterName).
  2. Définissez les propriétés et MaximumRowsParameterName les StartRowIndexParameterName propriétés ObjectDataSource en conséquence, StartRowIndexParameterName et MaximumRowsParameterName indiquent les noms des paramètres d’entrée transmis à des SelectMethod fins de pagination personnalisées. Par défaut, ces noms de paramètres sont startIndexRow et maximumRows, c’est pourquoi, lors de la création de la GetProductsPaged méthode dans la BLL, j’ai utilisé ces valeurs pour les paramètres d’entrée. Si vous avez choisi d’utiliser différents noms de paramètres pour la méthode BLL, GetProductsPaged par maxRowsstartIndex exemple, vous devez définir les propriétés et MaximumRowsParameterName les propriétés ObjectDataSource StartRowIndexParameterName en conséquence (par exemple, startIndex pour StartRowIndexParameterName et maxRows pour MaximumRowsParameterName).
  3. Définissez la propriété ObjectDataSource sur SelectCountMethod le nom de la méthode qui renvoie le nombre total d’enregistrements mis en page (TotalNumberOfProducts) rappelez-vous que la ProductsBLL méthode s TotalNumberOfProducts de classe retourne le nombre total d’enregistrements paginés à l’aide d’une méthode DAL qui exécute une SELECT COUNT(*) FROM Products requête. Ces informations sont requises par ObjectDataSource pour restituer correctement l’interface de pagination.
  4. Supprimez les éléments et<asp:Parameter> maximumRowsles startRowIndex éléments du balisage déclaratif de ObjectDataSource lors de la configuration de ObjectDataSource via l’Assistant, Visual Studio a automatiquement ajouté deux <asp:Parameter> éléments pour les paramètres d’entrée de la GetProductsPaged méthode. En définissant EnablePaging truesur , ces paramètres sont transmis automatiquement ; s’ils apparaissent également dans la syntaxe déclarative, ObjectDataSource tente de passer quatre paramètres à la GetProductsPaged méthode et deux paramètres à la TotalNumberOfProducts méthode. Si vous oubliez de supprimer ces <asp:Parameter> éléments, lors de la visite de la page via un navigateur, vous obtenez un message d’erreur comme : ObjectDataSource « ObjectDataSource1 » n’a pas pu trouver une méthode non générique « TotalNumberOfProducts » qui a des paramètres : startRowIndex, maximumRows.

Après avoir apporté ces modifications, la syntaxe déclarative de ObjectDataSource doit ressembler à ce qui suit :

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

Notez que les propriétés et SelectCountMethod les EnablePaging propriétés ont été définies et que les <asp:Parameter> éléments ont été supprimés. La figure 16 montre une capture d’écran du Fenêtre Propriétés une fois ces modifications effectuées.

Pour utiliser la pagination personnalisée, configurez le contrôle ObjectDataSource

Figure 16 : Pour utiliser la pagination personnalisée, configurez le contrôle ObjectDataSource

Après avoir apporté ces modifications, visitez cette page via un navigateur. Vous devriez voir 10 produits répertoriés, classés par ordre alphabétique. Prenez un moment pour parcourir les données d’une page à la fois. Bien qu’il n’existe aucune différence visuelle du point de vue de l’utilisateur final entre la pagination par défaut et la pagination personnalisée, la pagination personnalisée plus efficacement via de grandes quantités de données, car elle récupère uniquement ces enregistrements qui doivent être affichés pour une page donnée.

Les données, triées par le nom du produit, sont paginées à l’aide de la pagination personnalisée

Figure 17 : Les données, triées par nom du produit, sont paginées à l’aide de la pagination personnalisée (cliquez pour afficher l’image de taille complète)

Remarque

Avec la pagination personnalisée, la valeur de nombre de SelectCountMethod pages retournée par les ObjectDataSource est stockée dans l’état d’affichage de GridView. D’autres variables GridView sont SelectedIndexEditIndexDataKeys PageIndexstockées dans l’état de contrôle, et ainsi de suite sont conservées, quelle que soit la valeur de la propriété de EnableViewState GridView. Étant donné que la PageCount valeur est conservée dans les postbacks à l’aide de l’état d’affichage, lors de l’utilisation d’une interface de pagination qui inclut un lien pour vous amener à la dernière page, il est impératif que l’état d’affichage de GridView soit activé. (Si votre interface de pagination n’inclut pas de lien direct vers la dernière page, vous pouvez désactiver l’état d’affichage.)

Cliquer sur le dernier lien de page entraîne une publication et indique à GridView de mettre à jour sa PageIndex propriété. Si le dernier lien de page est cliqué, GridView affecte sa PageIndex propriété à une valeur inférieure à sa PageCount propriété. Lorsque l’état d’affichage est désactivé, la PageCount valeur est perdue entre les postbacks et la PageIndex valeur entière maximale est affectée à la place. Ensuite, GridView tente de déterminer l’index de ligne de départ en multipliant les propriétés et PageCount les PageSize propriétés. Ainsi, OverflowException le produit dépasse la taille entière maximale autorisée.

Implémenter la pagination et le tri personnalisés

Notre implémentation de pagination personnalisée actuelle nécessite que l’ordre par lequel les données sont paginées soit spécifié statiquement lors de la création de la GetProductsPaged procédure stockée. Toutefois, vous avez peut-être remarqué que la balise active gridView contient une case à cocher Activer le tri en plus de l’option Activer la pagination. Malheureusement, l’ajout de la prise en charge du tri à GridView avec notre implémentation de pagination personnalisée actuelle trie uniquement les enregistrements sur la page de données actuellement consultée. Par exemple, si vous configurez GridView pour prendre également en charge la pagination, puis, lors de l’affichage de la première page de données, triez par nom de produit dans l’ordre décroissant, il inverse l’ordre des produits sur la page 1. Comme le montre la figure 18, les Tigres carnarvon sont les premiers produits lors du tri dans l’ordre alphabétique inverse, ce qui ignore les 71 autres produits qui viennent après les Tigres carnarvon, par ordre alphabétique ; seuls ces enregistrements de la première page sont considérés dans le tri.

Seules les données affichées sur la page active sont triées

Figure 18 : Seules les données affichées sur la page active sont triées (cliquez pour afficher l’image pleine taille)

Le tri s’applique uniquement à la page active des données, car le tri se produit une fois que les données ont été récupérées à partir de la méthode BLL s GetProductsPaged , et cette méthode retourne uniquement ces enregistrements pour la page spécifique. Pour implémenter correctement le tri, nous devons passer l’expression de tri à la GetProductsPaged méthode afin que les données puissent être classées correctement avant de retourner la page spécifique des données. Nous allons voir comment procéder dans notre prochain tutoriel.

Implémentation de la pagination et de la suppression personnalisées

Si vous activez la suppression de fonctionnalités dans un GridView dont les données sont paginées à l’aide de techniques de pagination personnalisées, vous constaterez que lors de la suppression du dernier enregistrement de la dernière page, GridView disparaît plutôt que de décrémenter correctement les éléments GridView PageIndex. Pour reproduire ce bogue, activez la suppression pour le didacticiel uniquement que nous venons de créer. Accédez à la dernière page (page 9), où vous devriez voir un seul produit, car nous paginons jusqu’à 81 produits, 10 produits à la fois. Supprimez ce produit.

Lors de la suppression du dernier produit, GridView doit accéder automatiquement à la huitième page, et de telles fonctionnalités sont exposées avec la pagination par défaut. Toutefois, avec la pagination personnalisée après la suppression de ce dernier produit sur la dernière page, GridView disparaît simplement de l’écran. La raison précise pour laquelle cela se produit est un peu au-delà de l’étendue de ce didacticiel ; consultez La suppression du dernier enregistrement sur la dernière page à partir d’un GridView avec pagination personnalisée pour obtenir les détails de bas niveau en fonction de la source de ce problème. En résumé, il s’agit de la séquence suivante d’étapes effectuées par GridView lorsque le bouton Supprimer est cliqué :

  1. Supprimer l’enregistrement
  2. Obtenir les enregistrements appropriés à afficher pour l’objet PageIndex spécifié et PageSize
  3. Vérifiez que le PageIndex nombre de pages de données dans la source de données n’est pas dépassé ; si c’est le cas, décrémentez automatiquement la propriété GridView PageIndex
  4. Lier la page de données appropriée à GridView à l’aide des enregistrements obtenus à l’étape 2

Le problème découle du fait que, à l’étape 2, l’utilisation PageIndex des enregistrements à afficher est toujours la PageIndex dernière page dont le seul enregistrement a été supprimé. Par conséquent, à l’étape 2, aucun enregistrement n’est retourné depuis cette dernière page de données ne contient plus d’enregistrements. Ensuite, à l’étape 3, GridView réalise que sa PageIndex propriété est supérieure au nombre total de pages de la source de données (depuis que nous avons supprimé le dernier enregistrement de la dernière page) et décrémente donc sa PageIndex propriété. À l’étape 4, GridView tente de se lier aux données récupérées à l’étape 2 ; Toutefois, à l’étape 2, aucun enregistrement n’a été retourné, ce qui entraîne une grille GridView vide. Avec la pagination par défaut, ce problème ne s’affiche pas, car à l’étape 2 , tous les enregistrements sont récupérés à partir de la source de données.

Pour résoudre ce problème, nous avons deux options. La première consiste à créer un gestionnaire d’événements pour le gestionnaire d’événements RowDeleted GridView qui détermine le nombre d’enregistrements affichés dans la page qui vient d’être supprimé. S’il n’y avait qu’un seul enregistrement, l’enregistrement juste supprimé doit avoir été le dernier et nous devons décrémenter les éléments GridView s PageIndex. Bien sûr, nous voulons uniquement mettre à jour si PageIndex l’opération de suppression a réussi, ce qui peut être déterminé en s’assurant que la e.Exception propriété est null.

Cette approche fonctionne, car elle met à jour l’étape PageIndex 1 après, mais avant l’étape 2. Par conséquent, à l’étape 2, l’ensemble approprié d’enregistrements est retourné. Pour ce faire, utilisez du code comme suit :

protected void GridView1_RowDeleted(object sender, GridViewDeletedEventArgs e)
{
    // If we just deleted the last row in the GridView, decrement the PageIndex
    if (e.Exception == null && GridView1.Rows.Count == 1)
        // we just deleted the last row
        GridView1.PageIndex = Math.Max(0, GridView1.PageIndex - 1);
}

Une autre solution de contournement consiste à créer un gestionnaire d’événements pour l’événement ObjectDataSource RowDeleted et à définir la AffectedRows propriété sur la valeur 1. Après avoir supprimé l’enregistrement à l’étape 1 (mais avant de récupérer à nouveau les données à l’étape 2), GridView met à jour sa PageIndex propriété si une ou plusieurs lignes ont été affectées par l’opération. Toutefois, la AffectedRows propriété n’est pas définie par ObjectDataSource. Par conséquent, cette étape est omise. Une façon d’exécuter cette étape consiste à définir manuellement la AffectedRows propriété si l’opération de suppression se termine correctement. Pour ce faire, vous pouvez utiliser du code comme suit :

protected void ObjectDataSource1_Deleted(
    object sender, ObjectDataSourceStatusEventArgs e)
{
    // If we get back a Boolean value from the DeleteProduct method and it's true,
    // then we successfully deleted the product. Set AffectedRows to 1
    if (e.ReturnValue is bool && ((bool)e.ReturnValue) == true)
        e.AffectedRows = 1;
}

Le code de ces deux gestionnaires d’événements se trouve dans la classe code-behind de l’exemple EfficientPaging.aspx .

Comparaison des performances de la pagination par défaut et personnalisée

Étant donné que la pagination personnalisée récupère uniquement les enregistrements nécessaires, tandis que la pagination par défaut retourne tous les enregistrements pour chaque page en cours d’affichage, il est clair que la pagination personnalisée est plus efficace que la pagination par défaut. Mais combien plus efficace est la pagination personnalisée ? Quels sont les gains de performances visibles en passant de la pagination par défaut à la pagination personnalisée ?

Malheureusement, il n’y a aucune taille qui correspond à toutes les réponses ici. Le gain de performances dépend d’un certain nombre de facteurs, les deux principaux étant le nombre d’enregistrements paginés et la charge placée sur le serveur de base de données et les canaux de communication entre le serveur web et le serveur de base de données. Pour les petites tables avec seulement quelques dizaines d’enregistrements, la différence de performances peut être négligeable. Pour les grandes tables, avec des milliers à des centaines de milliers de lignes, cependant, la différence de performances est aiguë.

Un article de mine , « Pagination personnalisée dans ASP.NET 2.0 avec SQL Server 2005 », contient certains tests de performances que j’ai exécutés pour présenter les différences de performances entre ces deux techniques de pagination lors de la pagination dans une table de base de données avec 50 000 enregistrements. Dans ces tests, j’ai examiné le temps d’exécuter la requête au niveau SQL Server (à l’aide de SQL Profiler) et sur la page ASP.NET à l’aide des fonctionnalités de traçage de ASP.NET. Gardez à l’esprit que ces tests ont été exécutés sur ma zone de développement avec un seul utilisateur actif, et par conséquent sont non fiables et n’imitent pas les modèles de chargement de site web classiques. Quels que soient les résultats, les résultats illustrent les différences relatives dans le temps d’exécution pour la pagination par défaut et personnalisée lors de l’utilisation de quantités suffisamment importantes de données.

Moyenne. Durée (s) Reads
Pagination par défaut du profileur SQL 1.411 383
Profileur SQL de pagination personnalisé 0.002 29
Pagination par défaut ASP.NET trace 2.379 N/A
Pagination personnalisée ASP.NET Trace 0,029 N/A

Comme vous pouvez le voir, la récupération d’une page particulière de données nécessite 354 lectures inférieures en moyenne et terminées en une fraction du temps. Sur la page ASP.NET, la page personnalisée a pu s’afficher près du 1/100 du temps nécessaire lors de l’utilisation de la pagination par défaut.

Résumé

La pagination par défaut est un cinch pour implémenter uniquement la case à cocher Activer la pagination dans la balise active du contrôle Web de données, mais cette simplicité est fournie au coût des performances. Avec la pagination par défaut, lorsqu’un utilisateur demande n’importe quelle page de données , tous les enregistrements sont retournés, même si une petite fraction d’entre eux peut être affichée. Pour lutter contre cette surcharge de performances, ObjectDataSource offre une autre option de pagination personnalisée.

Bien que la pagination personnalisée s’améliore lors des problèmes de performances de pagination par défaut en récupérant uniquement les enregistrements qui doivent être affichés, il est plus impliqué pour implémenter la pagination personnalisée. Tout d’abord, une requête doit être écrite qui accède correctement (et efficacement) au sous-ensemble spécifique d’enregistrements demandés. Cela peut être accompli de plusieurs façons ; celle que nous avons examinée dans ce tutoriel consiste à utiliser la nouvelle ROW_NUMBER() fonction de SQL Server 2005 pour classer les résultats, puis à retourner uniquement ces résultats dont le classement se situe dans une plage spécifiée. En outre, nous devons ajouter un moyen pour déterminer le nombre total d’enregistrements paginés. Après avoir créé ces méthodes DAL et BLL, nous devons également configurer ObjectDataSource afin qu’il puisse déterminer le nombre total d’enregistrements en cours de pagination et passer correctement les valeurs d’index de ligne de début et de nombre maximal de lignes à la BLL.

Bien que l’implémentation de la pagination personnalisée nécessite un certain nombre d’étapes et n’est pas aussi simple que la pagination par défaut, la pagination personnalisée est une nécessité lors de la pagination de suffisamment grandes quantités de données. Comme l’ont montré les résultats examinés, la pagination personnalisée peut perdre des secondes du temps de rendu de la page ASP.NET et peut alléger la charge sur le serveur de base de données par un ou plusieurs ordres de grandeur.

Bonne programmation !

À propos de l’auteur

Scott Mitchell, auteur de sept livres ASP/ASP.NET et fondateur de 4GuysFromRolla.com, travaille avec les technologies Web Microsoft depuis 1998. Scott travaille en tant que consultant indépendant, formateur et écrivain. Son dernier livre est Sams Teach Yourself ASP.NET 2.0 en 24 heures. Il peut être accessible à mitchell@4GuysFromRolla.com. ou via son blog, qui peut être trouvé à http://ScottOnWriting.NET.