Esercitazione: Aggiungere ordinamento, filtro e paging - ASP.NET MVC con EF Core
Nell'esercitazione precedente è stato implementato un set di pagine Web per operazioni CRUD di base per le entità Student. In questa esercitazione si aggiungeranno le funzionalità di ordinamento, filtro e suddivisione in pagine alla pagina Student Index (Indice degli studenti). Verrà anche creata una pagina che esegue il raggruppamento semplice.
La figura seguente illustra 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 per Student Index
- Aggiungere la suddivisione in pagine al metodo Index
- Aggiungere collegamenti per la suddivisione in pagine
- Creare una pagina About
Prerequisiti
Aggiungere collegamenti per l'ordinamento delle colonne
Per aggiungere l'ordinamento alla pagina Student Index (Indice degli studenti), sarà necessario modificare il metodo Index
del controller Students e aggiungere codice alla visualizzazione Student Index (Indice degli studenti).
Aggiungere la funzionalità di ordinamento al metodo Index
In StudentsController.cs
sostituire il Index
metodo con il codice seguente:
public async Task<IActionResult> Index(string sortOrder)
{
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
var students = from s in _context.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(await students.AsNoTracking().ToListAsync());
}
Questo codice riceve un parametro sortOrder
dalla stringa di query nell'URL. Il valore della stringa di query viene inviato da ASP.NET Core MVC come parametro al metodo di azione. Il parametro sarà una stringa "Name" o "Date", seguito facoltativamente 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 al cognome, che è il valore predefinito determinato dal caso di fallthrough 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.
I due elementi ViewData
(NameSortParm e DateSortParm) vengono usati dalla visualizzazione per configurare i collegamenti ipertestuali dell'intestazione di colonna con i valori della stringa di query appropriata.
public async Task<IActionResult> Index(string sortOrder)
{
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
var students = from s in _context.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(await students.AsNoTracking().ToListAsync());
}
Si tratta di istruzioni ternarie. La prima specifica che, se il parametro sortOrder
è Null o vuoto, 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 | decrescente | ascending |
Cognome in ordine decrescente | ascending | ascending |
Data in ordine crescente | ascending | decrescente |
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 variabile IQueryable
prima dell'istruzione switch, la modifica nell'istruzione switch e chiama il metodo ToListAsync
dopo l'istruzione switch
. Quando si creano e modificano variabili IQueryable
, nessuna query viene inviata al database. La query non viene eseguita finché l'oggetto IQueryable
non viene convertito in una raccolta chiamando un metodo, ad esempio ToListAsync
. Questo codice genera pertanto una singola query che non viene eseguita fino all'istruzione return View
.
Questo codice può essere reso dettagliato con un numero elevato di colonne. Nell'ultima esercitazione di questa serie viene illustrato come scrivere codice che consente di passare il nome della colonna OrderBy
in una variabile di stringa.
Aggiungere collegamenti ipertestuali delle intestazioni di colonna alla visualizzazione Student Index (Indice degli studenti)
Sostituire il codice in Views/Students/Index.cshtml
con il codice seguente per aggiungere collegamenti ipertestuali all'intestazione di colonna. Le righe modificate sono evidenziate.
@model IEnumerable<ContosoUniversity.Models.Student>
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]">@Html.DisplayNameFor(model => model.LastName)</a>
</th>
<th>
@Html.DisplayNameFor(model => model.FirstMidName)
</th>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]">@Html.DisplayNameFor(model => model.EnrollmentDate)</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
@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>
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="@item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
Questo codice usa le informazioni contenute nelle proprietà ViewData
per impostare i collegamenti ipertestuali con i valori della stringa di query appropriati.
Eseguire l'app, selezionare la scheda Students (Studenti) e fare clic sulle intestazioni di colonna Last Name (Cognome) e Enrollment Date (Data di iscrizione) per verificare che l'ordinamento funzioni correttamente.
Aggiungere una casella di ricerca
Per aggiungere filtri alla pagina Student Index (Indice degli studenti), è necessario aggiungere alla visualizzazione una casella di testo e un pulsante di invio e apportare le modifiche corrispondenti nel metodo Index
. La casella di testo consente di immettere una stringa per eseguire la ricerca nei campi di nome e cognome.
Aggiungere la funzionalità di filtro al metodo Index
In StudentsController.cs
sostituire il Index
metodo con il codice seguente (le modifiche sono evidenziate).
public async Task<IActionResult> Index(string sortOrder, string searchString)
{
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
ViewData["CurrentFilter"] = searchString;
var students = from s in _context.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(await students.AsNoTracking().ToListAsync());
}
È stato aggiunto un parametro searchString
al metodo Index
. Il valore della stringa di ricerca viene ricevuto da una casella di testo che verrà aggiunta alla visualizzazione Index (Indice). È stata anche aggiunta all'istruzione LINQ una clausola where che seleziona solo gli studenti il cui nome o cognome contiene la stringa di ricerca. L'istruzione che aggiunge la clausola where viene eseguita solo se è presente un valore per la ricerca.
Nota
In questo esempio si chiama il metodo Where
su un oggetto IQueryable
e il filtro verrà elaborato nel server. In alcuni scenari potrebbe essere chiamato il metodo Where
come metodo di estensione per una raccolta in memoria. Si supponga, ad esempio, di modificare il riferimento a in modo che anziché un'istanza di Entity Framework DbSet
faccia riferimento a _context.Students
un metodo del repository che restituisce una IEnumerable
raccolta. Il risultato sarebbe normalmente lo stesso, ma in alcuni casi potrebbe essere diverso.
Ad esempio, l'implementazione di .NET Framework del metodo Contains
esegue un confronto con la distinzione tra maiuscole e minuscole per impostazione predefinita, ma in SQL Server questo è determinato dall'impostazione delle regole di confronto dell'istanza di SQL Server. Questa impostazione usa come valore predefinito la non applicazione della distinzione tra maiuscole e minuscole. È possibile chiamare il ToUpper
metodo per rendere il test senza distinzione tra maiuscole e minuscole in modo esplicito: Where(s => s.LastName.ToUpper(). Contains(searchString.ToUpper()). Questo fa sì che i risultati rimangano invariati se si modifica il codice in un secondo momento per usare un repository che restituisce una raccolta IEnumerable
invece di un oggetto IQueryable
. Quando si chiama il Contains
metodo in una IEnumerable
raccolta, si ottiene l'implementazione di .NET Framework. Quando viene chiamata su un IQueryable
oggetto, si ottiene l'implementazione del provider di database. Tuttavia, si verifica una riduzione delle prestazioni per questa soluzione. Il codice ToUpper
dovrà inserire una funzione nella clausola WHERE dell'istruzione TSQL SELECT. In questo modo si evita che l'ottimizzazione usi un indice. Dato che SQL viene installato per lo più con l'impostazione senza distinzione tra maiuscole e minuscole, è consigliabile evitare il codice ToUpper
fino a quando non si esegue la migrazione a un archivio con distinzione tra maiuscole e minuscole.
Aggiungere una casella di ricerca alla visualizzazione Student Index (Indice degli studenti)
In Views/Student/Index.cshtml
aggiungere il codice evidenziato immediatamente prima del tag della tabella di apertura per creare un didascalia, una casella di testo e un pulsante Cerca.
<p>
<a asp-action="Create">Create New</a>
</p>
<form asp-action="Index" method="get">
<div class="form-actions no-color">
<p>
Find by name: <input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" />
<input type="submit" value="Search" class="btn btn-default" /> |
<a asp-action="Index">Back to Full List</a>
</p>
</div>
</form>
<table class="table">
Questo codice usa l'helper tag<form>
per aggiungere la casella di testo e il pulsante di ricerca. Per impostazione predefinita, l'helper tag <form>
invia i dati del modulo con una richiesta 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 consigliano di usare l'istruzione GET quando l'azione non risulta in un aggiornamento.
Eseguire l'app, selezionare la scheda Students (Studenti), immettere una stringa di ricerca e fare clic su Search (Ricerca) per verificare che il filtro funzioni.
Si noti che l'URL contiene la stringa di ricerca.
http://localhost:5813/Students?SearchString=an
Se questa pagina viene inserita tra i segnalibri, quando si usa il segnalibro viene visualizzato l'elenco filtrato. L'aggiunta di method="get"
al tag form
è l'elemento che ha determinato la generazione della stringa di query.
In questa fase, se si fa clic sul collegamento di ordinamento di un'intestazione di colonna si perderà il valore del filtro immesso nella casella Search (Ricerca). Nella sezione successiva verrà spiegato come correggere il problema.
Aggiungere la suddivisione in pagine per Student Index
Per aggiungere la suddivisione in pagine alla pagina Student Index (Indice degli studenti), creare una classe PaginatedList
che usa le istruzioni Skip
e Take
per filtrare i dati sul server invece di recuperare sempre tutte le righe della tabella. È quindi possibile apportare altre modifiche nel metodo Index
e aggiungere i pulsanti di suddivisione in pagine alla visualizzazione Index
. Nella figura seguente vengono illustrati i pulsanti di suddivisione in pagine.
Nella cartella del progetto, creare PaginatedList.cs
e quindi sostituire il codice del modello con il codice seguente.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
namespace ContosoUniversity
{
public class PaginatedList<T> : List<T>
{
public int PageIndex { get; private set; }
public int TotalPages { get; private set; }
public PaginatedList(List<T> items, int count, int pageIndex, int pageSize)
{
PageIndex = pageIndex;
TotalPages = (int)Math.Ceiling(count / (double)pageSize);
this.AddRange(items);
}
public bool HasPreviousPage => PageIndex > 1;
public bool HasNextPage => PageIndex < TotalPages;
public static async Task<PaginatedList<T>> CreateAsync(IQueryable<T> source, int pageIndex, int pageSize)
{
var count = await source.CountAsync();
var items = await source.Skip((pageIndex - 1) * pageSize).Take(pageSize).ToListAsync();
return new PaginatedList<T>(items, count, pageIndex, pageSize);
}
}
}
Il metodo CreateAsync
in questo codice accetta le dimensioni di pagina e il numero delle pagine e applica le istruzioni Skip
e Take
appropriate a IQueryable
. Quando ToListAsync
viene chiamato su IQueryable
, restituisce un elenco contenente solo la pagina richiesta. Le proprietà HasPreviousPage
e HasNextPage
possono essere usate per abilitare o disabilitare i pulsanti di suddivisione in pagine Previous (Indietro) e Next (Avanti).
Viene usato un metodo CreateAsync
invece di un costruttore per creare l'oggetto PaginatedList<T>
poiché i costruttori non possono eseguire codice asincrono.
Aggiungere la suddivisione in pagine al metodo Index
In StudentsController.cs
sostituire il Index
metodo con il codice seguente.
public async Task<IActionResult> Index(
string sortOrder,
string currentFilter,
string searchString,
int? pageNumber)
{
ViewData["CurrentSort"] = sortOrder;
ViewData["NameSortParm"] = String.IsNullOrEmpty(sortOrder) ? "name_desc" : "";
ViewData["DateSortParm"] = sortOrder == "Date" ? "date_desc" : "Date";
if (searchString != null)
{
pageNumber = 1;
}
else
{
searchString = currentFilter;
}
ViewData["CurrentFilter"] = searchString;
var students = from s in _context.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;
}
int pageSize = 3;
return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));
}
Questo codice aggiunge un parametro di numeri di pagina, un parametro di ordinamento corrente e un parametro di filtro corrente alla firma del metodo.
public async Task<IActionResult> Index(
string sortOrder,
string currentFilter,
string searchString,
int? pageNumber)
La prima volta che viene visualizzata la pagina o se l'utente non ha selezionato un collegamento di suddivisione in pagine o di ordinamento, tutti i parametri saranno Null. Se si seleziona un collegamento di suddivisione in pagine, la variabile di pagina conterrà il numero della pagina da visualizzare.
L'elemento ViewData
denominato CurrentSort offre la visualizzazione con l'ordinamento corrente, in quanto esso deve essere incluso nei collegamenti di suddivisione in pagine per mantenere l'ordinamento durante la suddivisione in pagine.
L'elemento ViewData
denominato CurrentFilter offre 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 si immette un valore nella casella di testo e si preme il pulsante Invia. In tal caso, il parametro searchString
non è Null.
if (searchString != null)
{
pageNumber = 1;
}
else
{
searchString = currentFilter;
}
Alla fine del metodo Index
, il metodo PaginatedList.CreateAsync
converte la query degli studenti in una pagina singola di studenti in un tipo di raccolta che supporta la suddivisione in pagine. La pagina singola di studenti viene quindi passata alla visualizzazione.
return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));
Il metodo PaginatedList.CreateAsync
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 (pageNumber ?? 1)
significa restituzione del valore di pageNumber
se ha un valore oppure restituzione di 1 se pageNumber
è Null.
Aggiungere collegamenti per la suddivisione in pagine
In Views/Students/Index.cshtml
sostituire il codice esistente con il codice seguente. Le modifiche sono evidenziate.
@model PaginatedList<ContosoUniversity.Models.Student>
@{
ViewData["Title"] = "Index";
}
<h2>Index</h2>
<p>
<a asp-action="Create">Create New</a>
</p>
<form asp-action="Index" method="get">
<div class="form-actions no-color">
<p>
Find by name: <input type="text" name="SearchString" value="@ViewData["CurrentFilter"]" />
<input type="submit" value="Search" class="btn btn-default" /> |
<a asp-action="Index">Back to Full List</a>
</p>
</div>
</form>
<table class="table">
<thead>
<tr>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["NameSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Last Name</a>
</th>
<th>
First Name
</th>
<th>
<a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter="@ViewData["CurrentFilter"]">Enrollment Date</a>
</th>
<th></th>
</tr>
</thead>
<tbody>
@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>
<a asp-action="Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-action="Details" asp-route-id="@item.ID">Details</a> |
<a asp-action="Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
@{
var prevDisabled = !Model.HasPreviousPage ? "disabled" : "";
var nextDisabled = !Model.HasNextPage ? "disabled" : "";
}
<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.PageIndex - 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @prevDisabled">
Previous
</a>
<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.PageIndex + 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @nextDisabled">
Next
</a>
L'istruzione @model
nella parte superiore della pagina specifica che la vista ottiene ora un oggetto PaginatedList<T>
anziché un oggetto List<T>
.
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:
<a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter ="@ViewData["CurrentFilter"]">Enrollment Date</a>
I pulsanti di suddivisione in pagine vengono visualizzati dagli helper tag:
<a asp-action="Index"
asp-route-sortOrder="@ViewData["CurrentSort"]"
asp-route-pageNumber="@(Model.PageIndex - 1)"
asp-route-currentFilter="@ViewData["CurrentFilter"]"
class="btn btn-default @prevDisabled">
Previous
</a>
Eseguire l'app e passare alla pagina Students (Studenti).
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.
- Creare il metodo About nel Home controller.
- Creare la visualizzazione About.
Creare il modello di visualizzazione
Creare una cartella SchoolViewModels nella cartella Models.
Nella nuova cartella aggiungere un file EnrollmentDateGroup.cs
di classe e sostituire il codice del modello con il codice seguente:
using System;
using System.ComponentModel.DataAnnotations;
namespace ContosoUniversity.Models.SchoolViewModels
{
public class EnrollmentDateGroup
{
[DataType(DataType.Date)]
public DateTime? EnrollmentDate { get; set; }
public int StudentCount { get; set; }
}
}
Modificare il Home controller
In HomeController.cs
aggiungere le istruzioni using seguenti all'inizio del file:
using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Data;
using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.Extensions.Logging;
Aggiungere una variabile di classe per il contesto del database immediatamente dopo la parentesi graffa di apertura per la classe e ottenere un'istanza del contesto da ASP.NET Core DI:
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
private readonly SchoolContext _context;
public HomeController(ILogger<HomeController> logger, SchoolContext context)
{
_logger = logger;
_context = context;
}
Aggiungere un metodo About
con il codice seguente:
public async Task<ActionResult> About()
{
IQueryable<EnrollmentDateGroup> data =
from student in _context.Students
group student by student.EnrollmentDate into dateGroup
select new EnrollmentDateGroup()
{
EnrollmentDate = dateGroup.Key,
StudentCount = dateGroup.Count()
};
return View(await data.AsNoTracking().ToListAsync());
}
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
.
Creare la visualizzazione About
Aggiungere un Views/Home/About.cshtml
file con il codice seguente:
@model IEnumerable<ContosoUniversity.Models.SchoolViewModels.EnrollmentDateGroup>
@{
ViewData["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 passare alla pagina About. Il numero di studenti per ogni data di registrazione viene visualizzato in una tabella.
Ottenere il codice
Scaricare o visualizzare l'applicazione completata.
Passaggi successivi
In questa esercitazione:
- Aggiungere collegamenti per l'ordinamento delle colonne
- Aggiungere una casella di ricerca
- Aggiungere la suddivisione in pagine per Student Index
- Aggiungere la suddivisione in pagine al metodo Index
- Aggiungere collegamenti per la suddivisione in pagine
- Creare una pagina About
Passare all'esercitazione successiva per apprendere come gestire le modifiche al modello di dati tramite le migrazioni.
Commenti e suggerimenti
https://aka.ms/ContentUserFeedback.
Presto disponibile: Nel corso del 2024 verranno gradualmente disattivati i problemi di GitHub come meccanismo di feedback per il contenuto e ciò verrà sostituito con un nuovo sistema di feedback. Per altre informazioni, vedereInvia e visualizza il feedback per