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.

Students_Index_page_with_paging

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

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 LastNamea , 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.

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

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

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

  1. 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>
    
  2. 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 IEnumerablee 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.

  1. Dal menu Strumenti selezionare Gestione pacchetti NuGet e quindi Console di Gestione pacchetti.

  2. 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
    
  3. 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

  1. In Controllers\StudentController.cs aggiungere un'istruzione using per lo spazio dei PagedList nomi:

    using PagedList;
    
  2. 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, il searchString parametro non è Null.

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

    Alla fine del metodo, il ToPagedList metodo di estensione sull'oggetto students IQueryable 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 di page se ha un valore oppure restituzione di 1 se page è Null.

  1. 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 oggetto PagedList anziché un oggetto List.

    L'istruzione using per PagedList.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 e Model.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.

  2. 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 nel Home 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

  1. In HomeController.cs aggiungere le istruzioni seguenti using all'inizio del file:

    using ContosoUniversity.DAL;
    using ContosoUniversity.ViewModels;
    
  2. 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();
    
  3. 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.

  4. Aggiungere un Dispose metodo:

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

Modificare la visualizzazione della pagina About (Informazioni)

  1. 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>
    
  2. Eseguire l'app e fare clic sul collegamento Informazioni.

    Il numero di studenti per ogni data di registrazione viene visualizzato in una tabella.

    About_page

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.