Samouczek: dodawanie sortowania, filtrowania i stronicowania — ASP.NET MVC za pomocą polecenia EF Core
W poprzednim samouczku zaimplementowano zestaw stron internetowych dla podstawowych operacji CRUD dla jednostek Student. W tym samouczku dodasz funkcje sortowania, filtrowania i stronicowania do strony Indeks uczniów. Utworzysz również stronę, która wykonuje proste grupowanie.
Poniższa ilustracja przedstawia wygląd strony po zakończeniu pracy. Nagłówki kolumn to linki, które użytkownik może kliknąć, aby posortować według tej kolumny. Kliknięcie nagłówka kolumny wielokrotnie przełącza się między kolejnością sortowania rosnącego i malejącego.
W tym samouczku zostały wykonane następujące czynności:
- Dodawanie łączy sortowania kolumn
- Dodawanie pola wyszukiwania
- Dodawanie stronicowania do indeksu uczniów
- Dodawanie stronicowania do metody Index
- Dodawanie łączy stronicowania
- Tworzenie strony Informacje
Wymagania wstępne
Dodawanie łączy sortowania kolumn
Aby dodać sortowanie do strony Indeks uczniów, zmienisz Index
metodę kontrolera Students i dodasz kod do widoku Indeks ucznia.
Dodawanie funkcji sortowania do metody Index
W StudentsController.cs
pliku zastąp metodę Index
następującym kodem:
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());
}
Ten kod odbiera sortOrder
parametr z ciągu zapytania w adresie URL. Wartość ciągu zapytania jest dostarczana przez ASP.NET Core MVC jako parametr metody akcji. Parametr będzie ciągiem "Name" lub "Date", po którym opcjonalnie następuje podkreślenie i ciąg "desc", aby określić kolejność malejącą. Domyślna kolejność sortowania jest rosnąca.
Przy pierwszym żądaniu strony Indeks nie ma ciągu zapytania. Uczniowie są wyświetlani w kolejności rosnącej według nazwiska, która jest domyślna określona przez przypadek fall-through w instrukcji switch
. Gdy użytkownik kliknie hiperlink nagłówka kolumny, w ciągu zapytania zostanie podana odpowiednia sortOrder
wartość.
Dwa ViewData
elementy (NameSortParm i DateSortParm) są używane przez widok do konfigurowania hiperlinków nagłówków kolumn z odpowiednimi wartościami ciągu zapytania.
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());
}
Są toternarne instrukcje. Pierwszy określa, że jeśli sortOrder
parametr ma wartość null lub jest pusty, parametr NameSortParm powinien być ustawiony na wartość "name_desc"; w przeciwnym razie należy ustawić go na pusty ciąg. Te dwie instrukcje umożliwiają widokowi ustawienie hiperlinków nagłówka kolumny w następujący sposób:
Bieżąca kolejność sortowania | Hiperłącze nazwiska | Hiperłącze daty |
---|---|---|
Nazwisko rosnąco | malejąco | ascending |
Nazwisko malejące | ascending | ascending |
Data rosnąca | ascending | malejąco |
Data malejąco | ascending | ascending |
Metoda używa linQ to Entities, aby określić kolumnę do sortowania. Kod tworzy zmienną przed instrukcją IQueryable
switch, modyfikuje ją w instrukcji switch i wywołuje ToListAsync
metodę po instrukcji switch
. Podczas tworzenia i modyfikowania IQueryable
zmiennych żadne zapytanie nie jest wysyłane do bazy danych. Zapytanie nie jest wykonywane do momentu przekonwertowania IQueryable
obiektu na kolekcję przez wywołanie metody takiej jak ToListAsync
. W związku z tym ten kod powoduje utworzenie pojedynczego zapytania, które nie zostanie wykonane do momentu wykonania instrukcji return View
.
Ten kod może uzyskać pełne informacje z dużą liczbą kolumn. W ostatnim samouczku z tej serii pokazano, jak napisać kod, który umożliwia przekazanie nazwy OrderBy
kolumny w zmiennej ciągu.
Dodawanie hiperlinków nagłówków kolumn do widoku Indeks ucznia
Zastąp kod w pliku Views/Students/Index.cshtml
poniższym kodem, aby dodać hiperlinki nagłówka kolumny. Zmienione wiersze są wyróżnione.
@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>
Ten kod używa informacji we ViewData
właściwościach do konfigurowania hiperlinków z odpowiednimi wartościami ciągu zapytania.
Uruchom aplikację, wybierz kartę Uczniowie , a następnie kliknij nagłówki kolumn Last Name (Nazwisko ) i Enrollment Date (Data rejestracji), aby sprawdzić, czy sortowanie działa.
Dodawanie pola wyszukiwania
Aby dodać filtrowanie do strony Indeks uczniów, dodasz pole tekstowe i przycisk prześlij do widoku i wprowadzisz odpowiednie zmiany w metodzie Index
. Pole tekstowe umożliwi wprowadzenie ciągu do wyszukania w polach imienia i nazwiska.
Dodawanie funkcji filtrowania do metody Index
W StudentsController.cs
pliku zastąp metodę Index
następującym kodem (zmiany są wyróżnione).
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());
}
searchString
Dodano parametr do Index
metody . Wartość ciągu wyszukiwania jest odbierana z pola tekstowego, które zostanie dodane do widoku Indeks. Dodano również do instrukcji LINQ klauzulę where, która wybiera tylko uczniów, których imię lub nazwisko zawiera ciąg wyszukiwania. Instrukcja, która dodaje klauzulę where jest wykonywana tylko wtedy, gdy istnieje wartość do wyszukania.
Uwaga
W tym miejscu wywołujesz metodę Where
IQueryable
w obiekcie, a filtr zostanie przetworzony na serwerze. W niektórych scenariuszach możesz wywołać Where
metodę jako metodę rozszerzenia w kolekcji w pamięci. (Załóżmy na przykład, że zmienisz odwołanie na _context.Students
tak, aby zamiast programu EF DbSet
odwołuje się do metody repozytorium zwracającej IEnumerable
kolekcję). Wynik zwykle będzie taki sam, ale w niektórych przypadkach może być inny.
Na przykład implementacja Contains
programu .NET Framework metody domyślnie wykonuje porównanie z uwzględnieniem wielkości liter, ale w programie SQL Server jest to określane przez ustawienie sortowania wystąpienia programu SQL Server. To ustawienie powoduje, że ustawienie jest domyślnie niewrażliwe na wielkość liter. Możesz wywołać metodę ToUpper
, aby jawnie bez uwzględniania wielkości liter w teście: Where(s> =s.LastName.ToUpper(). Contains(searchString.ToUpper()).. Dzięki temu wyniki pozostaną takie same, jeśli później zmienisz kod, aby użyć repozytorium, które zwraca IEnumerable
kolekcję zamiast IQueryable
obiektu. (Podczas wywoływania Contains
metody w IEnumerable
kolekcji uzyskujesz implementację programu .NET Framework. Podczas wywoływania jej w IQueryable
obiekcie uzyskujesz implementację dostawcy bazy danych). Jednak w tym rozwiązaniu występuje kara za wydajność. Kod ToUpper
umieści funkcję w klauzuli WHERE instrukcji SELECT języka TSQL. Uniemożliwiłoby to optymalizatorowi korzystanie z indeksu. Ze względu na to, że język SQL jest najczęściej instalowany jako bez uwzględniania wielkości liter, najlepiej jest unikać ToUpper
kodu do momentu migracji do magazynu danych z uwzględnieniem wielkości liter.
Dodawanie pola wyszukiwania do widoku indeksu ucznia
W Views/Student/Index.cshtml
pliku dodaj wyróżniony kod bezpośrednio przed tagiem otwierającej tabelę, aby utworzyć podpis, pole tekstowe i przycisk Wyszukaj.
<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">
Ten kod używa pomocnika tagów <form>
do dodania pola tekstowego i przycisku wyszukiwania. Domyślnie pomocnik tagów <form>
przesyła dane formularza z postem, co oznacza, że parametry są przekazywane w treści komunikatu HTTP, a nie w adresie URL jako ciągi zapytania. Po określeniu żądania HTTP GET dane formularza są przekazywane w adresie URL jako ciągi zapytania, co umożliwia użytkownikom tworzenie zakładek adresu URL. Wytyczne W3C zaleca, aby użyć polecenia GET, gdy akcja nie spowoduje aktualizacji.
Uruchom aplikację, wybierz kartę Uczniowie , wprowadź ciąg wyszukiwania, a następnie kliknij pozycję Wyszukaj, aby sprawdzić, czy filtrowanie działa.
Zwróć uwagę, że adres URL zawiera ciąg wyszukiwania.
http://localhost:5813/Students?SearchString=an
Jeśli oznaczysz tę stronę zakładką, po użyciu zakładki uzyskasz filtrowaną listę. Dodanie method="get"
do tagu form
powoduje wygenerowanie ciągu zapytania.
Na tym etapie po kliknięciu linku sortowania nagłówka kolumny utracisz wartość filtru wprowadzoną w polu Wyszukiwania . Naprawisz to w następnej sekcji.
Dodawanie stronicowania do indeksu uczniów
Aby dodać stronicowanie do strony Indeks uczniów, utworzysz klasę PaginatedList
, która używa Skip
instrukcji i Take
do filtrowania danych na serwerze, zamiast zawsze pobierać wszystkie wiersze tabeli. Następnie wprowadzisz dodatkowe zmiany w metodzie Index
i dodasz przyciski stronicowania do Index
widoku. Na poniższej ilustracji przedstawiono przyciski stronicowania.
W folderze projektu utwórz PaginatedList.cs
plik , a następnie zastąp kod szablonu następującym kodem.
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);
}
}
}
Metoda CreateAsync
w tym kodzie przyjmuje rozmiar strony i numer strony oraz stosuje odpowiednie Skip
instrukcje i Take
do .IQueryable
Po ToListAsync
wywołaniu metody w obiekcie IQueryable
funkcja zwróci listę zawierającą tylko żądaną stronę. Właściwości HasPreviousPage
i HasNextPage
mogą służyć do włączania lub wyłączania przycisków Wstecz i Dalej stronicowania.
Metoda CreateAsync
jest używana zamiast konstruktora do utworzenia PaginatedList<T>
obiektu, ponieważ konstruktory nie mogą uruchamiać kodu asynchronicznego.
Dodawanie stronicowania do metody Index
W StudentsController.cs
pliku zastąp metodę Index
poniższym kodem.
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));
}
Ten kod dodaje parametr numeru strony, bieżący parametr kolejności sortowania i bieżący parametr filtru do podpisu metody.
public async Task<IActionResult> Index(
string sortOrder,
string currentFilter,
string searchString,
int? pageNumber)
Przy pierwszym wyświetleniu strony lub jeśli użytkownik nie kliknął łącza stronicowania lub sortowania, wszystkie parametry będą mieć wartość null. Jeśli klikniesz link stronicowania, zmienna strony będzie zawierać numer strony do wyświetlenia.
Element ViewData
o nazwie CurrentSort udostępnia widok z bieżącą kolejnością sortowania, ponieważ musi być uwzględniony w łączach stronicowania, aby zachować kolejność sortowania tak samo podczas stronicowania.
Element ViewData
o nazwie CurrentFilter udostępnia widok z bieżącym ciągiem filtru. Ta wartość musi być uwzględniona w linkach stronicowania, aby zachować ustawienia filtru podczas stronicowania i należy ją przywrócić do pola tekstowego po ponownym uruchomieniu strony.
Jeśli ciąg wyszukiwania zostanie zmieniony podczas stronicowania, strona musi zostać zresetowana do wartości 1, ponieważ nowy filtr może spowodować wyświetlenie różnych danych. Ciąg wyszukiwania jest zmieniany po wprowadzeniu wartości w polu tekstowym i naciśnięciu przycisku Prześlij. W takim przypadku searchString
parametr nie ma wartości null.
if (searchString != null)
{
pageNumber = 1;
}
else
{
searchString = currentFilter;
}
Na końcu Index
metody PaginatedList.CreateAsync
metoda konwertuje zapytanie ucznia na jedną stronę uczniów w typie kolekcji, który obsługuje stronicowanie. Ta pojedyncza strona uczniów jest następnie przekazywana do widoku.
return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));
Metoda PaginatedList.CreateAsync
przyjmuje numer strony. Dwa znaki zapytania reprezentują operator łączenia wartości null. Operator łączenia wartości null definiuje wartość domyślną dla typu dopuszczającego wartość null; wyrażenie (pageNumber ?? 1)
oznacza, że zwraca wartość pageNumber
, jeśli ma wartość lub zwraca wartość 1, jeśli pageNumber
ma wartość null.
Dodawanie łączy stronicowania
W Views/Students/Index.cshtml
pliku zastąp istniejący kod następującym kodem. Zmiany są wyróżnione.
@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>
Instrukcja @model
w górnej części strony określa, że widok pobiera PaginatedList<T>
teraz obiekt zamiast List<T>
obiektu.
Linki nagłówka kolumny używają ciągu zapytania, aby przekazać bieżący ciąg wyszukiwania do kontrolera, aby użytkownik mógł sortować wyniki filtru:
<a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter ="@ViewData["CurrentFilter"]">Enrollment Date</a>
Przyciski stronicowania są wyświetlane przez pomocników tagów:
<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>
Uruchom aplikację i przejdź do strony Uczniowie.
Kliknij łącza stronicowania w różnych kolejności sortowania, aby upewnić się, że stronicowanie działa. Następnie wprowadź ciąg wyszukiwania i spróbuj ponownie stronicować, aby sprawdzić, czy stronicowanie działa również poprawnie z sortowaniem i filtrowaniem.
Tworzenie strony Informacje
Na stronie Informacje o witrynie internetowej Contoso University zobaczysz liczbę studentów zarejestrowanych dla każdej daty rejestracji. Wymaga to grupowania i prostych obliczeń w grupach. W tym celu wykonasz następujące czynności:
- Utwórz klasę modelu widoku dla danych, które należy przekazać do widoku.
- Utwórz metodę About w kontrolerze Home .
- Utwórz widok Informacje.
Tworzenie modelu widoku
Utwórz folder SchoolViewModels w folderze Models.
W nowym folderze dodaj plik EnrollmentDateGroup.cs
klasy i zastąp kod szablonu następującym kodem:
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; }
}
}
Modyfikowanie Home kontrolera
W HomeController.cs
pliku dodaj następujące instrukcje using w górnej części pliku:
using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Data;
using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.Extensions.Logging;
Dodaj zmienną klasy dla kontekstu bazy danych bezpośrednio po otwarciu nawiasu klamrowego dla klasy i pobierz wystąpienie kontekstu z 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;
}
Dodaj metodę About
z następującym kodem:
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());
}
Instrukcja LINQ grupuje jednostki uczniów według daty rejestracji, oblicza liczbę jednostek w każdej grupie i przechowuje wyniki w kolekcji EnrollmentDateGroup
obiektów modelu widoku.
Tworzenie widoku Informacje
Views/Home/About.cshtml
Dodaj plik z następującym kodem:
@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>
Uruchom aplikację i przejdź do strony Informacje. Liczba uczniów dla każdej daty rejestracji jest wyświetlana w tabeli.
Uzyskiwanie kodu
Pobierz lub wyświetl ukończoną aplikację.
Następne kroki
W tym samouczku zostały wykonane następujące czynności:
- Dodano łącza sortowania kolumn
- Dodano pole wyszukiwania
- Dodano stronicowanie do indeksu uczniów
- Dodano stronicowanie do metody Index
- Dodano łącza stronicowania
- Utworzono stronę Informacje
Przejdź do następnego samouczka, aby dowiedzieć się, jak obsługiwać zmiany modelu danych przy użyciu migracji.
Opinia
https://aka.ms/ContentUserFeedback.
Dostępne już wkrótce: W 2024 r. będziemy stopniowo wycofywać zgłoszenia z serwisu GitHub jako mechanizm przesyłania opinii na temat zawartości i zastępować go nowym systemem opinii. Aby uzyskać więcej informacji, sprawdź:Prześlij i wyświetl opinię dla