Tutoriel : Ajouter le tri, le filtrage et la pagination avec Entity Framework dans une application MVC ASP.NET

Dans le tutoriel précédent, vous avez implémenté un ensemble de pages web pour les opérations CRUD de base pour les Student entités. Dans ce tutoriel, vous allez ajouter des fonctionnalités de tri, de filtrage et de pagination à la page Index des étudiants . Vous créez également une page de regroupement simple.

L’image suivante montre à quoi ressemblera la page lorsque vous avez terminé. Les en-têtes des colonnes sont des liens sur lesquels l’utilisateur peut cliquer pour trier selon les colonnes. Cliquer de façon répétée sur un en-tête de colonne permet de changer l’ordre de tri (croissant ou décroissant).

Students_Index_page_with_paging

Dans ce tutoriel, vous allez :

  • Ajouter des liens de tri de colonne
  • Ajouter une zone Rechercher
  • Ajouter la fonctionnalité de pagination
  • Créer une page À propos

Prérequis

Pour ajouter le tri à la page Index étudiant, vous allez modifier la Index méthode du contrôleur et ajouter du Student code à la Student vue Index.

Ajouter une fonctionnalité de tri à la méthode Index

  • Dans Controllers\StudentController.cs, remplacez la Index méthode par le code suivant :

    public ActionResult Index(string sortOrder)
    {
       ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
       ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
       var students = from s in db.Students
                      select s;
       switch (sortOrder)
       {
          case "name_desc":
             students = students.OrderByDescending(s => s.LastName);
             break;
          case "Date":
             students = students.OrderBy(s => s.EnrollmentDate);
             break;
          case "date_desc":
             students = students.OrderByDescending(s => s.EnrollmentDate);
             break;
          default:
             students = students.OrderBy(s => s.LastName);
             break;
       }
       return View(students.ToList());
    }
    

Ce code reçoit un paramètre sortOrder à partir de la chaîne de requête dans l’URL. La valeur de chaîne de requête est fournie par ASP.NET MVC en tant que paramètre de la méthode d’action. Le paramètre est une chaîne « Name » ou « Date », éventuellement suivie d’un trait de soulignement et de la chaîne « desc » pour spécifier l’ordre décroissant. L’ordre de tri par défaut est croissant.

La première fois que la page d’index est demandée, il n’y a pas de chaîne de requête. Les étudiants sont affichés dans l’ordre croissant par LastName, qui est la valeur par défaut établie par le cas de repli dans l’instruction switch . Quand l’utilisateur clique sur un lien hypertexte d’en-tête de colonne, la valeur sortOrder appropriée est fournie dans la chaîne de requête.

Les deux ViewBag variables sont utilisées pour que la vue puisse configurer les liens hypertexte de titre de colonne avec les valeurs de chaîne de requête appropriées :

ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";

Il s’agit d’instructions ternaires. La première spécifie que si le sortOrder paramètre est null ou vide, ViewBag.NameSortParm doit être défini sur « name_desc » ; sinon, il doit être défini sur une chaîne vide. Ces deux instructions permettent à la vue de définir les liens hypertexte d’en-tête de colonne comme suit :

Ordre de tri actuel Lien hypertexte Nom de famille Lien hypertexte Date
Nom de famille croissant descending ascending
Nom de famille décroissant ascending ascending
Date croissante ascending descending
Date décroissante ascending ascending

La méthode utilise LINQ to Entities pour spécifier la colonne à trier. Le code crée une IQueryable<T> variable avant l’instruction, la switch modifie dans l’instruction switch et appelle la ToList méthode après l’instruction switch . Lorsque vous créez et modifiez des variables IQueryable, aucune requête n’est envoyée à la base de données. La requête n’est pas exécutée tant que vous n’avez pas converti l’objet IQueryable en collection en appelant une méthode telle que ToList. Par conséquent, ce code aboutit à une requête unique qui n’est pas exécutée tant que l’instruction n’est return View pas exécutée.

En guise d’alternative à l’écriture d’instructions LINQ différentes pour chaque ordre de tri, vous pouvez créer dynamiquement une instruction LINQ. Pour plus d’informations sur LINQ dynamique, consultez LINQ dynamique.

  1. Dans Views\Student\Index.cshtml, remplacez les <tr> éléments et <th> de la ligne de titre par le code mis en surbrillance :

    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    <table class="table">
        <tr>
            <th>
                @Html.ActionLink("Last Name", "Index", new { sortOrder = ViewBag.NameSortParm })
            </th>
            <th>First Name
            </th>
            <th>
                @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm })
            </th>
            <th></th>
        </tr>
    
    @foreach (var item in Model) {
    

    Ce code utilise les informations contenues dans les ViewBag propriétés pour configurer des liens hypertexte avec les valeurs de chaîne de requête appropriées.

  2. Exécutez la page et cliquez sur les en-têtes de colonne Nom et Date d’inscription pour vérifier que le tri fonctionne.

    Une fois que vous avez cliqué sur l’en-tête Nom , les étudiants s’affichent dans l’ordre décroissant du nom de famille.

Pour ajouter le filtrage à la page d’index Étudiants, vous allez ajouter une zone de texte et un bouton Envoyer à la vue et apporter les modifications correspondantes dans la Index méthode. La zone de texte vous permet d’entrer une chaîne à rechercher dans les champs prénom et nom.

Ajouter la fonctionnalité de filtrage à la méthode Index

  • Dans Controllers\StudentController.cs, remplacez la Index méthode par le code suivant (les modifications sont mises en surbrillance) :

    public ViewResult Index(string sortOrder, string searchString)
    {
        ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
        ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
        var students = from s in db.Students
                       select s;
        if (!String.IsNullOrEmpty(searchString))
        {
            students = students.Where(s => s.LastName.Contains(searchString)
                                   || s.FirstMidName.Contains(searchString));
        }
        switch (sortOrder)
        {
            case "name_desc":
                students = students.OrderByDescending(s => s.LastName);
                break;
            case "Date":
                students = students.OrderBy(s => s.EnrollmentDate);
                break;
            case "date_desc":
                students = students.OrderByDescending(s => s.EnrollmentDate);
                break;
            default:
                students = students.OrderBy(s => s.LastName);
                break;
        }
    
        return View(students.ToList());
    }
    

Le code ajoute un searchString paramètre à la Index méthode . La valeur de chaîne de recherche est reçue à partir d’une zone de texte que vous ajouterez à la vue Index. Il ajoute également une where clause à l’instruction LINQ qui sélectionne uniquement les étudiants dont le prénom ou le nom contient la chaîne de recherche. L’instruction qui ajoute la Where clause s’exécute uniquement s’il existe une valeur à rechercher.

Notes

Dans de nombreux cas, vous pouvez appeler la même méthode sur un jeu d’entités Entity Framework ou en tant que méthode d’extension sur une collection en mémoire. Les résultats sont normalement les mêmes, mais dans certains cas peuvent être différents.

Par exemple, l’implémentation .NET Framework de la Contains méthode retourne toutes les lignes lorsque vous lui passez une chaîne vide, mais le fournisseur Entity Framework pour SQL Server Compact 4.0 retourne zéro ligne pour les chaînes vides. Par conséquent, le code de l’exemple (placer l’instruction Where à l’intérieur d’une if instruction) garantit que vous obtenez les mêmes résultats pour toutes les versions de SQL Server. En outre, l’implémentation .NET Framework de la Contains méthode effectue une comparaison respectant la casse par défaut, mais les fournisseurs Entity Framework SQL Server effectuent des comparaisons sans respect de la casse par défaut. Par conséquent, l’appel de la ToUpper méthode pour rendre le test explicitement insensible à la casse garantit que les résultats ne changent pas lorsque vous modifiez le code ultérieurement pour utiliser un référentiel, ce qui retournera une IEnumerable collection au lieu d’un IQueryable objet. (Lorsque vous appelez la méthode Contains sur une collection IEnumerable, vous obtenez l’implémentation du .NET Framework ; lorsque vous l’appelez sur un objet IQueryable, vous obtenez l’implémentation du fournisseur de base de données.)

La gestion des valeurs Null peut également être différente pour différents fournisseurs de base de données ou lorsque vous utilisez un IQueryable objet par rapport à quand vous utilisez une IEnumerable collection. Par exemple, dans certains scénarios, une Where condition telle que ne table.Column != 0 peut pas retourner des colonnes qui ont null comme valeur. Par défaut, EF génère des opérateurs SQL supplémentaires pour que l’égalité entre les valeurs null fonctionne dans la base de données comme elle fonctionne en mémoire, mais vous pouvez définir l’indicateur UseDatabaseNullSemantics dans EF6 ou appeler la méthode UseRelationalNulls dans EF Core pour configurer ce comportement.

Ajouter une zone de recherche à la vue Index Étudiant

  1. Dans Views\Student\Index.cshtml, ajoutez le code en surbrillance immédiatement avant la balise d’ouverture table afin de créer un légende, une zone de texte et un bouton Rechercher.

    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    
    @using (Html.BeginForm())
    {
        <p>
            Find by name: @Html.TextBox("SearchString")  
            <input type="submit" value="Search" /></p>
    }
    
    <table>
        <tr>
    
  2. Exécutez la page, entrez une chaîne de recherche, puis cliquez sur Rechercher pour vérifier que le filtrage fonctionne.

    Notez que l’URL ne contient pas la chaîne de recherche « an », ce qui signifie que si vous marquez cette page, vous n’obtiendrez pas la liste filtrée lorsque vous utilisez le signet. Cela s’applique également aux liens de tri de colonne, car ils trient l’ensemble de la liste. Vous allez modifier le bouton Rechercher pour utiliser les chaînes de requête pour les critères de filtre plus loin dans le tutoriel.

Ajouter la fonctionnalité de pagination

Pour ajouter la pagination à la page d’index Étudiants, vous allez commencer par installer le package NuGet PagedList.Mvc . Ensuite, vous allez apporter des modifications supplémentaires à la Index méthode et ajouter des liens de pagination à la Index vue. PagedList.Mvc est l’un des nombreux bons packages de pagination et de tri pour ASP.NET MVC, et son utilisation ici est destinée uniquement à titre d’exemple, et non comme une recommandation pour elle par rapport à d’autres options.

Installer le package NuGet PagedList.MVC

Le package NuGet PagedList.Mvc installe automatiquement le package PagedList en tant que dépendance. Le package PagedList installe un type de collection et des méthodes d’extension PagedList pour IQueryable les collections et IEnumerable . Les méthodes d’extension créent une seule page de données dans une PagedList collection à partir de votre IQueryable ou IEnumerable, et la PagedList collection fournit plusieurs propriétés et méthodes qui facilitent la pagination. Le package PagedList.Mvc installe une assistance de pagination qui affiche les boutons de pagination.

  1. Dans le menu Outils , sélectionnez Gestionnaire de package NuGet , puis Console du Gestionnaire de package.

  2. Dans la fenêtre Console du Gestionnaire de package, vérifiez que la source du package est nuget.org et que le projet Par défaut est ContosoUniversity, puis entrez la commande suivante :

    Install-Package PagedList.Mvc
    
  3. Créez le projet.

Notes

Le package PageList n’est plus conservé. Par conséquent, pour les projets en cours, il est préférable d’utiliser le package X.PagedList . La main différence est que X.PagedList est un assembly portable. Cela signifie que le package est multiplateforme et peut être utilisé pour des projets web ainsi que d’autres projets .NET. Le nouveau package ne doit pas entraîner de problèmes de compatibilité, car il a été porté vers .NET 6 depuis la version 8.4.

Ajouter la fonctionnalité de pagination à la méthode Index

  1. Dans Controllers\StudentController.cs, ajoutez une using instruction pour l’espace de PagedList noms :

    using PagedList;
    
  2. Remplacez la méthode Index par le code suivant :

    public ViewResult Index(string sortOrder, string currentFilter, string searchString, int? page)
    {
       ViewBag.CurrentSort = sortOrder;
       ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
       ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
    
       if (searchString != null)
       {
          page = 1;
       }
       else
       {
          searchString = currentFilter;
       }
    
       ViewBag.CurrentFilter = searchString;
    
       var students = from s in db.Students
                      select s;
       if (!String.IsNullOrEmpty(searchString))
       {
          students = students.Where(s => s.LastName.Contains(searchString)
                                 || s.FirstMidName.Contains(searchString));
       }
       switch (sortOrder)
       {
          case "name_desc":
             students = students.OrderByDescending(s => s.LastName);
             break;
          case "Date":
             students = students.OrderBy(s => s.EnrollmentDate);
             break;
          case "date_desc":
             students = students.OrderByDescending(s => s.EnrollmentDate);
             break;
          default:  // Name ascending 
             students = students.OrderBy(s => s.LastName);
             break;
       }
    
       int pageSize = 3;
       int pageNumber = (page ?? 1);
       return View(students.ToPagedList(pageNumber, pageSize));
    }
    

    Ce code ajoute un page paramètre, un paramètre d’ordre de tri actuel et un paramètre de filtre actuel à la signature de méthode :

    public ActionResult Index(string sortOrder, string currentFilter, string searchString, int? page)
    

    La première fois que la page est affichée, ou si l’utilisateur n’a pas cliqué sur un lien de pagination ou de tri, tous les paramètres sont null. Si vous cliquez sur un lien de pagination, la page variable contient le numéro de page à afficher.

    Une ViewBag propriété fournit à l’affichage l’ordre de tri actuel, car il doit être inclus dans les liens de pagination afin de conserver l’ordre de tri identique lors de la pagination :

    ViewBag.CurrentSort = sortOrder;
    

    Une autre propriété, ViewBag.CurrentFilter, fournit à la vue la chaîne de filtre actuelle. Cette valeur doit être incluse dans les liens de changement de page pour que les paramètres de filtre soient conservés lors du changement de page, et elle doit être restaurée dans la zone de texte lorsque la page est réaffichée. Si la chaîne de recherche est modifiée au cours du changement de page, la page doit être réinitialisée à 1, car le nouveau filtre peut entraîner l’affichage de données différentes. La chaîne de recherche est modifiée lorsqu’une valeur est entrée dans la zone de texte et que vous appuyez sur le bouton Envoyer. Dans ce cas, le searchString paramètre n’est pas null.

    if (searchString != null)
    {
        page = 1;
    }
    else
    {
        searchString = currentFilter;
    }
    

    À la fin de la méthode, la ToPagedList méthode d’extension sur l’objet students IQueryable convertit la requête de l’étudiant en une seule page d’étudiants dans un type de collection qui prend en charge la pagination. Cette seule page d’étudiants est ensuite passée à la vue :

    int pageSize = 3;
    int pageNumber = (page ?? 1);
    return View(students.ToPagedList(pageNumber, pageSize));
    

    La méthode ToPagedList accepte un numéro de page. Les deux points d’interrogation représentent l’opérateur de fusion null. L’opérateur de fusion Null définit une valeur par défaut pour un type nullable ; l’expression (page ?? 1) indique de renvoyer la valeur de page si elle a une valeur, ou de renvoyer 1 si page a la valeur Null.

  1. Dans Views\Student\Index.cshtml, remplacez le code existant par le code suivant. Les modifications sont mises en surbrillance.

    @model PagedList.IPagedList<ContosoUniversity.Models.Student>
    @using PagedList.Mvc;
    <link href="~/Content/PagedList.css" rel="stylesheet" type="text/css" />
    
    @{
        ViewBag.Title = "Students";
    }
    
    <h2>Students</h2>
    
    <p>
        @Html.ActionLink("Create New", "Create")
    </p>
    @using (Html.BeginForm("Index", "Student", FormMethod.Get))
    {
        <p>
            Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
            <input type="submit" value="Search" />
        </p>
    }
    <table class="table">
        <tr>
            <th>
                @Html.ActionLink("Last Name", "Index", new { sortOrder = ViewBag.NameSortParm, currentFilter=ViewBag.CurrentFilter })
            </th>
            <th>
                First Name
            </th>
            <th>
                @Html.ActionLink("Enrollment Date", "Index", new { sortOrder = ViewBag.DateSortParm, currentFilter=ViewBag.CurrentFilter })
            </th>
            <th></th>
        </tr>
    
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.LastName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.FirstMidName)
            </td>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                @Html.ActionLink("Edit", "Edit", new { id=item.ID }) |
                @Html.ActionLink("Details", "Details", new { id=item.ID }) |
                @Html.ActionLink("Delete", "Delete", new { id=item.ID })
            </td>
        </tr>
    }
    
    </table>
    <br />
    Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount
    
    @Html.PagedListPager(Model, page => Url.Action("Index", 
        new { page, sortOrder = ViewBag.CurrentSort, currentFilter = ViewBag.CurrentFilter }))
    

    L’instruction @model en haut de la page spécifie que la vue obtient désormais un objet PagedList à la place d’un objet List.

    L’instruction using pour PagedList.Mvc donne accès à l’assistance MVC pour les boutons de pagination.

    Le code utilise une surcharge de BeginForm qui lui permet de spécifier FormMethod.Get.

    @using (Html.BeginForm("Index", "Student", FormMethod.Get))
    {
        <p>
            Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)  
            <input type="submit" value="Search" />
        </p>
    }
    

    BeginForm par défaut envoie des données de formulaire avec un post, ce qui signifie que les paramètres sont passés dans le corps du message HTTP et non dans l’URL en tant que chaînes de requête. Lorsque vous spécifiez HTTP GET, les données de formulaire sont transmises dans l’URL sous forme de chaînes de requête, ce qui permet aux utilisateurs d’ajouter l’URL aux favoris. Les instructions du W3C relatives à l’utilisation de HTTP GET recommandent d’utiliser GET lorsque l’action n’entraîne pas de mise à jour.

    La zone de texte étant initialisée avec la chaîne de recherche actuelle, lorsque vous cliquez sur une nouvelle page, vous pouvez voir la chaîne de recherche actuelle.

    Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
    

    Les liens d’en-tête de colonne utilisent la chaîne de requête pour transmettre la chaîne de recherche actuelle au contrôleur afin que l’utilisateur puisse trier les résultats de filtrage :

    @Html.ActionLink("Last Name", "Index", new { sortOrder=ViewBag.NameSortParm, currentFilter=ViewBag.CurrentFilter })
    

    La page active et le nombre total de pages sont affichés.

    Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount
    

    S’il n’y a aucune page à afficher, « Page 0 sur 0 » s’affiche. (Dans ce cas, le numéro de page est supérieur au nombre de pages, car Model.PageNumber est 1 et Model.PageCount est égal à 0.)

    Les boutons de pagination sont affichés par l’assistance PagedListPager :

    @Html.PagedListPager( Model, page => Url.Action("Index", new { page }) )
    

    L’assistance PagedListPager fournit un certain nombre d’options que vous pouvez personnaliser, notamment les URL et le style. Pour plus d’informations, consultez TroyGoode / PagedList sur le site GitHub.

  2. Exécutez la page.

    Cliquez sur les liens de changement de page dans différents ordres de tri pour vérifier que le changement de page fonctionne. Ensuite, entrez une chaîne de recherche et essayez de changer de page à nouveau pour vérifier que le changement de page fonctionne correctement avec le tri et le filtrage.

Créer une page À propos

Pour la page About du site web de Contoso University, vous afficherez le nombre d’étudiants inscrits pour chaque date d’inscription. Cela nécessite un regroupement et des calculs simples sur les groupes. Pour ce faire, vous devez effectuer les opérations suivantes :

  • Créez une classe de modèle de vue pour les données que vous devez transmettre à la vue.
  • Modifiez la About méthode dans le Home contrôleur.
  • Modifiez la About vue.

Créer le modèle d’affichage

Créez un dossier ViewModels dans le dossier du projet. Dans ce dossier, ajoutez un fichier de classe EnrollmentDateGroup.cs et remplacez le code du modèle par le code suivant :

using System;
using System.ComponentModel.DataAnnotations;

namespace ContosoUniversity.ViewModels
{
    public class EnrollmentDateGroup
    {
        [DataType(DataType.Date)]
        public DateTime? EnrollmentDate { get; set; }

        public int StudentCount { get; set; }
    }
}

Modifier le contrôleur Home

  1. Dans HomeController.cs, ajoutez les instructions suivantes using en haut du fichier :

    using ContosoUniversity.DAL;
    using ContosoUniversity.ViewModels;
    
  2. Ajoutez une variable de classe pour le contexte de base de données immédiatement après l’accolade ouvrante pour la classe :

    public class HomeController : Controller
    {
        private SchoolContext db = new SchoolContext();
    
  3. Remplacez la méthode About par le code suivant :

    public ActionResult About()
    {
        IQueryable<EnrollmentDateGroup> data = from student in db.Students
                   group student by student.EnrollmentDate into dateGroup
                   select new EnrollmentDateGroup()
                   {
                       EnrollmentDate = dateGroup.Key,
                       StudentCount = dateGroup.Count()
                   };
        return View(data.ToList());
    }
    

    L’instruction LINQ regroupe les entités Student par date d’inscription, calcule le nombre d’entités dans chaque groupe et stocke les résultats dans une collection d’objets de modèle de vue EnrollmentDateGroup.

  4. Ajoutez une Dispose méthode :

    protected override void Dispose(bool disposing)
    {
        db.Dispose();
        base.Dispose(disposing);
    }
    

Modifier la vue About

  1. Remplacez le code dans le fichier Views\Home\About.cshtml par le code suivant :

    @model IEnumerable<ContosoUniversity.ViewModels.EnrollmentDateGroup>
               
    @{
        ViewBag.Title = "Student Body Statistics";
    }
    
    <h2>Student Body Statistics</h2>
    
    <table>
        <tr>
            <th>
                Enrollment Date
            </th>
            <th>
                Students
            </th>
        </tr>
    
    @foreach (var item in Model) {
        <tr>
            <td>
                @Html.DisplayFor(modelItem => item.EnrollmentDate)
            </td>
            <td>
                @item.StudentCount
            </td>
        </tr>
    }
    </table>
    
  2. Exécutez l’application et cliquez sur le lien À propos de .

    Le nombre d’étudiants pour chaque date d’inscription s’affiche dans un tableau.

    About_page

Obtenir le code

Télécharger le projet terminé

Ressources supplémentaires

Vous trouverez des liens vers d’autres ressources Entity Framework dans ASP.NET Accès aux données - Ressources recommandées.

Étapes suivantes

Dans ce tutoriel, vous allez :

  • Ajouter des liens de tri de colonne
  • Ajouter une zone Rechercher
  • Ajouter la fonctionnalité de pagination
  • Créer une page À propos

Passez à l’article suivant pour apprendre à utiliser la résilience de connexion et l’interception de commandes.