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).
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
Ajouter des liens de tri de colonne
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.
Ajouter des liens hypertexte d’en-tête de colonne à la vue d’index Étudiant
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.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.
Ajouter une zone Rechercher
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
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>
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.
Dans le menu Outils , sélectionnez Gestionnaire de package NuGet , puis Console du Gestionnaire de package.
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
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
Dans Controllers\StudentController.cs, ajoutez une
using
instruction pour l’espace dePagedList
noms :using PagedList;
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, lesearchString
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 studentsIQueryable
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 depage
si elle a une valeur, ou de renvoyer 1 sipage
a la valeur Null.
Ajouter des liens de pagination à l’affichage Index étudiant
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 objetPagedList
à la place d’un objetList
.L’instruction
using
pourPagedList.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 etModel.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.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 leHome
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
Dans HomeController.cs, ajoutez les instructions suivantes
using
en haut du fichier :using ContosoUniversity.DAL; using ContosoUniversity.ViewModels;
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();
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
.Ajoutez une
Dispose
méthode :protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); }
Modifier la vue About
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>
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.
Obtenir le code
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.
Commentaires
https://aka.ms/ContentUserFeedback.
Bientôt disponible : Tout au long de 2024, nous allons supprimer progressivement GitHub Issues comme mécanisme de commentaires pour le contenu et le remplacer par un nouveau système de commentaires. Pour plus d’informations, consultezEnvoyer et afficher des commentaires pour