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.

Students index page

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

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

Zastąp kod w pliku Views/Students/Index.cshtmlponiż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.

Students index page in name order

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.cspliku 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ę WhereIQueryable 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.cshtmlpliku 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.

Students index page with filtering

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.

Students index page with paging links

W folderze projektu utwórz PaginatedList.csplik , 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 IQueryablefunkcja 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.cspliku 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.

W Views/Students/Index.cshtmlpliku 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.

Students index page with paging links

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