Esercitazione: Aggiungere l'ordinamento, il filtro e il paging con Entity Framework in un'applicazione MVC ASP.NET
Nell'esercitazione precedente è stato implementato un set di pagine Web per le operazioni CRUD di base per Student
le entità. In questa esercitazione si aggiungono funzionalità di ordinamento, filtro e paging alla pagina Indice studenti . Si crea anche una pagina di raggruppamento semplice.
L'immagine seguente mostra l'aspetto della pagina al termine dell'operazione. Le intestazioni di colonna sono collegamenti su cui l'utente può fare clic per eseguire l'ordinamento in base alla colonna. Facendo clic più volte su un'intestazione di colonna è possibile passare dall'ordinamento crescente a quello decrescente e viceversa.
In questa esercitazione:
- Aggiungere collegamenti per l'ordinamento delle colonne
- Aggiungere una casella di ricerca
- Aggiungere la suddivisione in pagine
- Creare una pagina About
Prerequisiti
Aggiungere collegamenti per l'ordinamento delle colonne
Per aggiungere l'ordinamento alla pagina Student Index, si modificherà il metodo del Student
controller e si aggiungerà il Index
codice alla Student
visualizzazione Index.
Aggiungere la funzionalità di ordinamento al metodo Index
In Controllers\StudentController.cs sostituire il
Index
metodo con il codice seguente: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()); }
Questo codice riceve un parametro sortOrder
dalla stringa di query nell'URL. Il valore della stringa di query viene fornito da ASP.NET MVC come parametro per il metodo action. Il parametro è una stringa che è "Name" o "Date", facoltativamente seguita da un carattere di sottolineatura e dalla stringa "desc" per specificare l'ordine decrescente. Per impostazione predefinita, l'ordinamento è crescente.
La prima volta che viene richiesta la pagina di indice, non è presente alcuna stringa di query. Gli studenti vengono visualizzati in ordine crescente in base LastName
a , ovvero il valore predefinito stabilito dal caso di fall-through nell'istruzione switch
. Quando l'utente fa clic sul collegamento ipertestuale di un'intestazione di colonna, nella stringa di query viene specificato il valore sortOrder
appropriato.
Le due ViewBag
variabili vengono usate in modo che la vista possa configurare i collegamenti ipertestuali dell'intestazione di colonna con i valori della stringa di query appropriati:
ViewBag.NameSortParm = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewBag.DateSortParm = sortOrder == "Date" ? "date_desc" : "Date";
Si tratta di istruzioni ternarie. Il primo specifica che se il sortOrder
parametro è Null o vuoto, ViewBag.NameSortParm
deve essere impostato su "name_desc". In caso contrario, deve essere impostato su una stringa vuota. Queste due istruzioni consentono alla visualizzazione di impostare i collegamenti ipertestuali dell'intestazione di colonna come indicato di seguito:
Ordinamento corrente | Collegamento ipertestuale cognome | Collegamento ipertestuale data |
---|---|---|
Cognome in ordine crescente | descending | ascending |
Cognome in ordine decrescente | ascending | ascending |
Data in ordine crescente | ascending | descending |
Data in ordine decrescente | ascending | ascending |
Il metodo usa LINQ to Entities per specificare la colonna in base alla quale eseguire l'ordinamento. Il codice crea una IQueryable<T> variabile prima dell'istruzione switch
, la modifica nell'istruzione switch
e chiama il ToList
metodo dopo l'istruzione switch
. Quando si creano e modificano variabili IQueryable
, nessuna query viene inviata al database. La query non viene eseguita fino a quando non si converte l'oggetto IQueryable
in una raccolta chiamando un metodo come ToList
. Di conseguenza, questo codice genera una singola query che non viene eseguita fino all'istruzione return View
.
In alternativa alla scrittura di istruzioni LINQ diverse per ogni ordinamento, è possibile creare dinamicamente un'istruzione LINQ. Per informazioni su LINQ dinamico, vedere DYNAMIC LINQ.
Aggiungere collegamenti ipertestuali dell'intestazione di colonna alla visualizzazione Indice Student
In Views\Student\Index.cshtml sostituire gli
<tr>
elementi e<th>
per la riga di intestazione con il codice evidenziato:<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) {
Questo codice usa le informazioni nelle
ViewBag
proprietà per configurare i collegamenti ipertestuali con i valori della stringa di query appropriati.Eseguire la pagina e fare clic sulle intestazioni di colonna Cognome e Data di registrazione per verificare che l'ordinamento funzioni.
Dopo aver fatto clic sull'intestazione Cognome , gli studenti vengono visualizzati in ordine decrescente del cognome.
Aggiungere una casella di ricerca
Per aggiungere il filtro alla pagina Indice Studenti, aggiungere una casella di testo e un pulsante di invio alla visualizzazione e apportare le modifiche corrispondenti nel Index
metodo . La casella di testo consente di immettere una stringa da cercare nei campi nome e cognome.
Aggiungere la funzionalità di filtro al metodo Index
In Controllers\StudentController.cs sostituire il
Index
metodo con il codice seguente (le modifiche sono evidenziate):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()); }
Il codice aggiunge un searchString
parametro al Index
metodo . Il valore della stringa di ricerca viene ricevuto da una casella di testo che verrà aggiunta alla visualizzazione Index (Indice). Aggiunge inoltre una where
clausola all'istruzione LINQ che seleziona solo gli studenti il cui nome o cognome contiene la stringa di ricerca. L'istruzione che aggiunge la Where clausola viene eseguita solo se è presente un valore da cercare.
Nota
In molti casi è possibile chiamare lo stesso metodo su un set di entità di Entity Framework o come metodo di estensione in una raccolta in memoria. I risultati sono in genere uguali, ma in alcuni casi possono essere diversi.
Ad esempio, l'implementazione di .NET Framework del Contains
metodo restituisce tutte le righe quando si passa una stringa vuota, ma il provider Entity Framework per SQL Server Compact 4.0 restituisce zero righe per stringhe vuote. Pertanto, il codice nell'esempio (inserendo l'istruzione Where
all'interno di un'istruzioneif
) garantisce di ottenere gli stessi risultati per tutte le versioni di SQL Server. Inoltre, l'implementazione di .NET Framework del Contains
metodo esegue un confronto con distinzione tra maiuscole e minuscole per impostazione predefinita, ma i provider di Entity Framework SQL Server eseguono confronti senza distinzione tra maiuscole e minuscole per impostazione predefinita. Pertanto, la chiamata al ToUpper
metodo per rendere il test senza distinzione tra maiuscole e minuscole garantisce che i risultati non vengano modificati quando si modifica il codice in un secondo momento per usare un repository, che restituirà una IEnumerable
raccolta anziché un IQueryable
oggetto . Quando il metodo Contains
viene chiamato su una raccolta IEnumerable
, si ottiene l'implementazione di .NET Framework; quando viene chiamato su un oggetto IQueryable
, si ottiene l'implementazione del provider di database.
La gestione dei valori Null può anche essere diversa per provider di database diversi o quando si usa un IQueryable
oggetto rispetto a quando si usa una IEnumerable
raccolta. In alcuni scenari, ad esempio, una Where
condizione, table.Column != 0
ad esempio, potrebbe non restituire colonne con null
il valore . Per impostazione predefinita, EF genera operatori SQL aggiuntivi per eseguire l'uguaglianza tra valori Null nel database come funziona in memoria, ma è possibile impostare il flag UseDatabaseNullSemantics in EF6 o chiamare il metodo UseRelationalNulls in EF Core per configurare questo comportamento.
Aggiungere una casella di ricerca alla visualizzazione Indice student
In Views\Student\Index.cshtml aggiungere il codice evidenziato immediatamente prima del tag di apertura
table
per creare un didascalia, una casella di testo e un pulsante Cerca.<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>
Eseguire la pagina, immettere una stringa di ricerca e fare clic su Cerca per verificare che il filtro funzioni.
Si noti che l'URL non contiene la stringa di ricerca "an", il che significa che se si segnalibra questa pagina, non si otterrà l'elenco filtrato quando si usa il segnalibro. Questo vale anche per i collegamenti di ordinamento delle colonne, in quanto ordinano l'intero elenco. Il pulsante Cerca verrà modificato per usare le stringhe di query per i criteri di filtro più avanti nell'esercitazione.
Aggiungere la suddivisione in pagine
Per aggiungere il paging alla pagina indice Students, si inizierà installando il pacchetto NuGet PagedList.Mvc . Si modificheranno quindi altre modifiche nel Index
metodo e si aggiungeranno collegamenti di paging alla Index
visualizzazione. PagedList.Mvc è uno dei numerosi pacchetti di paging e ordinamento validi per ASP.NET MVC e l'uso qui è previsto solo come esempio, non come raccomandazione per le altre opzioni.
Installare il pacchetto NuGet PagedList.MVC
Il pacchetto NuGet PagedList.Mvc installa automaticamente il pacchetto PagedList come dipendenza. Il pacchetto PagedList installa un PagedList
tipo di raccolta e metodi di estensione per IQueryable
le raccolte e IEnumerable
. I metodi di estensione creano una singola pagina di dati in una PagedList
raccolta all'esterno di IQueryable
o IEnumerable
e la raccolta fornisce diverse proprietà e metodi che facilitano il PagedList
paging. Il pacchetto PagedList.Mvc installa un helper di paging che visualizza i pulsanti di paging.
Dal menu Strumenti selezionare Gestione pacchetti NuGet e quindi Console di Gestione pacchetti.
Nella finestra Console di Gestione pacchetti verificare che l'origine pacchetto sia nuget.org e che il progetto predefinito sia ContosoUniversity e quindi immettere il comando seguente:
Install-Package PagedList.Mvc
Compilare il progetto.
Nota
Il pacchetto PageList non viene più gestito. Pertanto, per i progetti correnti è preferibile usare il pacchetto X.PagedList . La differenza principale è che X.PagedList è un assembly portatile. Ciò significa che il pacchetto è multipiattaforma e può essere usato per i progetti Web e per altri progetti .NET. Il nuovo pacchetto non dovrebbe causare problemi di compatibilità, perché è stato convertito in .NET 6 dalla versione 8.4.
Aggiungere la funzionalità di suddivisione in pagine al metodo Index
In Controllers\StudentController.cs aggiungere un'istruzione
using
per lo spazio deiPagedList
nomi:using PagedList;
Sostituire il metodo
Index
con il codice seguente: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)); }
Questo codice aggiunge un
page
parametro, un parametro di ordinamento corrente e un parametro di filtro corrente alla firma del metodo:public ActionResult Index(string sortOrder, string currentFilter, string searchString, int? page)
La prima volta che viene visualizzata la pagina o se l'utente non ha fatto clic su un collegamento di paging o ordinamento, tutti i parametri sono Null. Se si fa clic su un collegamento di paging, la
page
variabile contiene il numero di pagina da visualizzare.Una
ViewBag
proprietà fornisce la visualizzazione con l'ordinamento corrente, perché deve essere inclusa nei collegamenti di paging per mantenere lo stesso ordinamento durante il paging:ViewBag.CurrentSort = sortOrder;
Un'altra proprietà,
ViewBag.CurrentFilter
, fornisce la visualizzazione con la stringa di filtro corrente. Questo valore deve essere incluso nei collegamenti di suddivisione in pagine per mantenere le impostazioni di filtro nella suddivisione in pagine e deve essere ripristinato nella casella di testo quando la pagina viene nuovamente visualizzata. Se la stringa di ricerca viene modificata nella suddivisione in pagine, la pagina deve essere reimpostata su 1, poiché il nuovo filtro può comportare la visualizzazione di dati diversi. La stringa di ricerca viene modificata quando viene immesso un valore nella casella di testo e viene premuto il pulsante di invio. In tal caso, ilsearchString
parametro non è Null.if (searchString != null) { page = 1; } else { searchString = currentFilter; }
Alla fine del metodo, il
ToPagedList
metodo di estensione sull'oggetto studentsIQueryable
converte la query student in una singola pagina di studenti in un tipo di raccolta che supporta il paging. La singola pagina degli studenti viene quindi passata alla visualizzazione:int pageSize = 3; int pageNumber = (page ?? 1); return View(students.ToPagedList(pageNumber, pageSize));
Il metodo
ToPagedList
accetta un numero di pagina. I due punti interrogativi rappresentano l'operatore null-coalescing. L'operatore null-coalescing definisce un valore predefinito per un tipo nullable. L'espressione(page ?? 1)
significa restituzione del valore dipage
se ha un valore oppure restituzione di 1 sepage
è Null.
Aggiungere collegamenti di paging alla visualizzazione indice Student
In Views\Student\Index.cshtml sostituire il codice esistente con il codice seguente. Le modifiche sono evidenziate.
@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'istruzione
@model
nella parte superiore della pagina specifica che la vista ottiene ora un oggettoPagedList
anziché un oggettoList
.L'istruzione
using
perPagedList.Mvc
fornisce l'accesso all'helper MVC per i pulsanti di paging.Il codice usa un overload di BeginForm che consente di specificare 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> }
Il valore predefinito BeginForm invia i dati del modulo con un POST, il che significa che i parametri vengono passati nel corpo del messaggio HTTP e non nell'URL come stringhe di query. Quando si specifica HTTP GET, i dati del modulo vengono passati nell'URL come stringhe di query, il che consente agli utenti di inserire l'URL tra i segnalibri. Le linee guida W3C per l'uso di HTTP GET consigliano di usare GET quando l'azione non genera un aggiornamento.
La casella di testo viene inizializzata con la stringa di ricerca corrente, quindi quando si fa clic su una nuova pagina è possibile visualizzare la stringa di ricerca corrente.
Find by name: @Html.TextBox("SearchString", ViewBag.CurrentFilter as string)
I collegamenti delle intestazioni di colonna usano la stringa di query per passare la stringa di ricerca corrente al controller in modo che l'utente possa procedere all'ordinamento all'interno dei risultati di filtro:
@Html.ActionLink("Last Name", "Index", new { sortOrder=ViewBag.NameSortParm, currentFilter=ViewBag.CurrentFilter })
Vengono visualizzati la pagina corrente e il numero totale di pagine.
Page @(Model.PageCount < Model.PageNumber ? 0 : Model.PageNumber) of @Model.PageCount
Se non sono presenti pagine da visualizzare, viene visualizzata la pagina 0 di 0. In tal caso il numero di pagina è maggiore del numero di pagine perché
Model.PageNumber
è 1 eModel.PageCount
è 0.I pulsanti di paging vengono visualizzati dall'helper
PagedListPager
:@Html.PagedListPager( Model, page => Url.Action("Index", new { page }) )
L'helper
PagedListPager
offre una serie di opzioni che è possibile personalizzare, inclusi URL e stili. Per altre informazioni, vedere TroyGoode/PagedList nel sito GitHub.Eseguire la pagina.
Fare clic sui collegamenti di suddivisione in pagine in diversi tipi di ordinamento per verificare che la suddivisione in pagine funzioni. Immettere quindi una stringa di ricerca e provare nuovamente la suddivisione in pagine per verificare che funzioni correttamente anche con l'ordinamento e il filtro.
Creare una pagina About
Per la pagina About (Informazioni) del sito Web Contoso University verrà visualizzato il numero di studenti iscritti per ogni data di iscrizione. Questa operazione richiede calcoli di raggruppamento e semplici sui gruppi. Per completare questa procedura, è necessario eseguire le operazioni seguenti:
- Creare una classe modello di visualizzazione per i dati che è necessario passare alla visualizzazione.
- Modificare il
About
metodo nelHome
controller. - Modificare la
About
visualizzazione.
Creare il modello di visualizzazione
Creare una cartella ViewModels nella cartella del progetto. In tale cartella aggiungere un file di classe EnrollmentDateGroup.cs e sostituire il codice del modello con il codice seguente:
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; }
}
}
Modificare il controller Home
In HomeController.cs aggiungere le istruzioni seguenti
using
all'inizio del file:using ContosoUniversity.DAL; using ContosoUniversity.ViewModels;
Aggiungere una variabile di classe per il contesto di database immediatamente dopo la parentesi graffa di apertura per la classe :
public class HomeController : Controller { private SchoolContext db = new SchoolContext();
Sostituire il metodo
About
con il codice seguente: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'istruzione LINQ raggruppa le entità di studenti per data di registrazione, calcola il numero di entità in ogni gruppo e archivia i risultati in una raccolta di oggetti di modello della visualizzazione
EnrollmentDateGroup
.Aggiungere un
Dispose
metodo:protected override void Dispose(bool disposing) { db.Dispose(); base.Dispose(disposing); }
Modificare la visualizzazione della pagina About (Informazioni)
Sostituire il codice nel file Views\Home\About.cshtml con il codice seguente:
@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>
Eseguire l'app e fare clic sul collegamento Informazioni.
Il numero di studenti per ogni data di registrazione viene visualizzato in una tabella.
Ottenere il codice
Scaricare il progetto completato
Risorse aggiuntive
I collegamenti ad altre risorse di Entity Framework sono disponibili in ASP.NET Accesso ai dati - Risorse consigliate.
Passaggi successivi
In questa esercitazione:
- Aggiungere collegamenti per l'ordinamento delle colonne
- Aggiungere una casella di ricerca
- Aggiungere la suddivisione in pagine
- Creare una pagina About
Passare all'articolo successivo per informazioni su come usare la resilienza della connessione e l'intercettazione dei comandi.
Commenti e suggerimenti
https://aka.ms/ContentUserFeedback.
Presto disponibile: nel corso del 2024 verranno dismessi i problemi di GitHub come meccanismo di feedback per il contenuto e verranno sostituiti con un nuovo sistema di feedback. Per altre informazioni, vedere:Invia e visualizza il feedback per