Tutorial: Hinzufügen von Sortieren, Filtern und Paging – ASP.NET MVC mit EF Core

Im vorherigen Tutorial haben Sie eine Reihe von Webseiten für grundlegende CRUD-Vorgänge für Studentenentitäten implementiert. In diesem Tutorial fügen Sie die Funktionen zum Sortieren, Filtern und Paging zur Studentenindexseite hinzu. Sie werden auch eine Seite erstellen, auf der einfache Gruppierungsvorgänge ausgeführt werden.

Die folgende Abbildung zeigt, wie die Seite am Ende aussehen wird. Die Spaltenüberschriften sind Links, auf die der Benutzer klicken kann, um die Spalte zu sortieren. Wiederholtes Klicken auf eine Spaltenüberschrift schaltet zwischen aufsteigender und absteigender Sortierreihenfolge um.

Students index page

In diesem Tutorial:

  • Hinzufügen von Spaltensortierungslinks
  • Hinzufügen eines Suchfelds
  • Hinzufügen von Paging zum „Students“-Index
  • Hinzufügen von Paging zur „Index“-Methode
  • Hinzufügen von Paginglinks
  • Erstellen einer Infoseite

Voraussetzungen

Ändern Sie die Index-Methode des Studentencontrollers, und fügen Sie Code zur Studentenindexansicht hinzu, um der Indexseite die Sortierfunktion hinzuzufügen.

Hinzufügen der Sortierfunktion zur Indexmethode

Ersetzen Sie in StudentsController.cs die Index-Methode durch folgenden Code:

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());
}

Dieser Code empfängt einen sortOrder-Parameter aus der Abfragezeichenfolge in der URL. Der Wert der Abfragezeichenfolge wird von ASP.NET Core MVC als Parameter an die Aktionsmethode übergeben. Der Parameter ist eine Zeichenfolge, entweder „Name“ oder „Date“, optional gefolgt von einem Unterstrich und der Zeichenfolge „desc“, die die absteigende Reihenfolge angibt. Standardmäßig wird eine aufsteigende Sortierreihenfolge verwendet.

Bei der ersten Anforderung der Indexseite gibt es keine Abfragezeichenfolge. Die Studenten werden nach Nachnamen in aufsteigender Reihenfolge angezeigt. Dies ist durch den Fall-Through-Fall in der switch-Anweisung standardmäßig festgelegt. Wenn der Benutzer auf den Link einer Spaltenüberschrift klickt, wird der entsprechende sortOrder-Wert in der Abfragezeichenfolge bereitgestellt.

Die beiden ViewData-Elemente (NameSortParm und DateSortParm) werden von der Ansicht verwendet, um die Links der Spaltenüberschriften mit den entsprechenden Abfragezeichenfolgenwerten zu konfigurieren.

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());
}

Hierbei handelt es sich um ternäre Anweisungen. Die erste gibt an, dass wenn der sortOrder-Parameter gleich 0 (null) oder leer ist, „NameSortParm“ auf „name_desc“ festgelegt werden soll. Andernfalls soll er auf eine leere Zeichenfolge festgelegt werden. Diese beiden Anweisungen ermöglichen der Ansicht das Festlegen der Links für Spaltenüberschriften wie folgt:

Aktuelle Sortierreihenfolge Hyperlink „Nachname“ Hyperlink „Datum“
Nachname (aufsteigend) descending ascending
Nachname (absteigend) ascending ascending
Datum (aufsteigend) ascending descending
Datum (absteigend) ascending ascending

Die Methode gibt über LINQ to Entities die Spalte an, nach der sortiert werden soll. Der Code erstellt vor der Switch-Anweisung eine IQueryable-Variable, ändert sie in der Switch-Anweisung und ruft die ToListAsync-Methode nach der switch-Anweisung auf. Es wir keine Abfrage an die Datenbank gesendet, wenn Sie die IQueryable-Variablen erstellen und ändern. Die Abfrage wird nicht ausgeführt, bis Sie das IQueryable-Objekt in eine Sammlung konvertieren, indem Sie eine Methode aufrufen, z.B. die ToListAsync-Methode. Aus diesem Grund führt dieser Code zu einer einzelnen Abfrage, die bis zur return View-Anweisung nicht ausgeführt wird.

Dieser Code könnte mit einer großen Anzahl von Spalten ausführlich werden. Das letzte Tutorial dieser Reihe zeigt, wie Sie Code schreiben, mit dem Sie den Namen der OrderBy-Spalte an eine Zeichenfolgenvariablen übergeben können.

Ersetzen Sie den Code in Views/Students/Index.cshtml durch den folgenden Code, um Spaltenüberschriftenlinks hinzuzufügen. Die geänderten Zeilen werden hervorgehoben.

@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>

Dieser Code verwendet die Informationen in den ViewData-Eigenschaften zum Einrichten von Links mit den entsprechenden Abfragezeichenfolgenwerten.

Führen Sie die Anwendung aus, wählen Sie die Registerkarte Students (Studenten) aus, klicken Sie auf die Spaltenüberschriften Last Name (Nachname) und Enrollment Date (Anmeldedatum), um diese Sortierung zu überprüfen.

Students index page in name order

Wenn Sie eine Filterfunktion zur Studentenindexseite hinzufügen möchten, dann fügen Sie ein Textfeld und die Schaltfläche „Senden“ zur Ansicht hinzu, und führen Sie die entsprechenden Änderungen in der Index-Methode aus. Sie können eine Zeichenfolge in das Textfeld für Vor- und Nachnamen eingeben, um eine Suche zu starten.

Hinzufügen der Filterfunktion zur Indexmethode

Ersetzen Sie in StudentsController.cs die Index-Methode durch folgenden Code (die Änderungen sind hervorgehoben).

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());
}

Sie haben einen searchString-Parameter zur Index-Methode hinzugefügt. Der Zeichenfolgenwert für die Suche wird aus einem Textfeld empfangen, das Sie zur Indexansicht hinzufügen. Sie haben ebenfalls eine Where-Klausel zur LINQ-Anweisung hinzugefügt, die nur Studenten auswählt, deren Vor- oder Nachnamen die zu suchende Zeichenfolge enthält. Die Anweisung, die die Where-Klausel hinzufügt, wird nur ausgeführt, wenn nach einem Wert gesucht wird.

Hinweis

Wenn Sie die Where-Methode auf einem IQueryable-Objekt aufrufen, wird der Filter auf dem Server verarbeitet. In einigen Szenarios rufen Sie möglicherweise die Where-Methode als Erweiterungsmethode für eine speicherinterne Sammlung auf. (Angenommen, Sie ändern den Verweis auf _context.Students. Dann wird nicht mehr auf ein DbSet-EF, sondern auf eine Repository-Methode verwiesen, die eine IEnumerable-Sammlung zurückgibt.) Das Ergebnis wäre normalerweise identisch, aber in einigen Fällen kann es unterschiedlich ausfallen.

Die .NET Framework-Implementierung der Contains-Methode führt beispielsweise standardmäßig einen Vergleich unter Beachtung der Groß-/Kleinschreibung durch. Aber in SQL Server wird dies durch die Sortierungseinstellung der SQL Server-Instanz bestimmt. Diese Einstellung berücksichtigt die Groß-/Kleinschreibung standardmäßig nicht. Sie können die ToUpper-Methode aufrufen, damit der Test die Groß-/Kleinschreibung explizit nicht berücksichtigt: Where(s => s.LastName.ToUpper().Contains(searchString.ToUpper()). Das würde sicherstellen, dass die Ergebnisse gleich bleiben, wenn Sie den Code später ändern, um ein Repository zu verwenden, das eine IEnumerable-Sammlung anstelle eines IQueryable-Objekts zurückgibt. (Beim Aufrufen der Contains-Methode einer IEnumerable-Sammlung erhalten Sie die .NET Framework-Implementierung. Wenn Sie sie auf einem IQueryable-Objekt aufrufen, erhalten Sie die Implementierung des Datenanbieters.) Es gibt jedoch eine Leistungseinbuße für diese Lösung. Der ToUpper-Code würde eine Funktion in die WHERE-Klausel der TSQL SELECT-Anweisung setzen. Die würde verhindern, dass der Optimierer einen Index verwendet. Da SQL die Groß-/Kleinschreibung hauptsächlich nicht berücksichtigt, wird empfohlen, den ToUpper-Code zu vermeiden, bis die Migration zu einem Datenspeicher erfolgt ist, der die Groß-/Kleinschreibung beachtet.

Hinzufügen eines Suchfelds zur Studentenindexansicht

Fügen Sie in Views/Student/Index.cshtml den hervorgehobenen Code unmittelbar vor dem Tag „Tabelle öffnen“ hinzu, um eine Beschriftung, ein Textfeld und eine Suche-Schaltfläche zu erstellen.

<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">

Dieser Code verwendet das Taghilfsprogramm<form>, um das Suchtextfeld und die Schaltfläche hinzuzufügen. Das Taghilfsprogramm <form> sendet standardmäßig Formulardaten mit einem POST, was bedeutet, dass Parameter als Abfragezeichenfolgen im Hauptteil der HTTP-Nachricht und nicht in der URL übergeben werden. Bei der Angabe von HTTP GET werden die Formulardaten als Abfragezeichenfolgen an die URL übergeben. Dadurch können Benutzer ein Lesezeichen für die URL erstellen. Die W3C-Richtlinien empfehlen die Verwendung eines GET-Vorgangs, wenn die Aktion nicht zu einem Update führt.

Führen Sie die Anwendung aus, wählen Sie die Registerkarte Studenten, geben Sie eine Suchzeichenfolge ein, und klicken Sie auf „Suchen“, um die Funktionsweise des Filters zu überprüfen.

Students index page with filtering

Beachten Sie, dass die URL die Suchzeichenfolge enthält.

http://localhost:5813/Students?SearchString=an

Wenn Sie diese Seite kennzeichnen, erhalten Sie die gefilterte Liste bei der Verwendung von Lesezeichen. Wird method="get" zum form-Tag hinzugefügt, wird die Abfragezeichenfolge generiert.

Wenn Sie in dieser Phase auf einen Sortierlink in einer Spaltenüberschrift klicken, verlieren Sie den Filterwert, den Sie im Feld neben Suchen eingegeben haben. Dies soll im nächsten Abschnitt behoben werden.

Hinzufügen von Paging zum „Students“-Index

Um die Pagingfunktionen zur Studentenindexseite hinzuzufügen, erstellen Sie eine PaginatedList-Klasse, die Skip- und Take-Anweisungen zum Filtern von Daten auf dem Server verwendet, anstatt immer alle Zeilen der Tabelle abzurufen. Dann nehmen Sie zusätzliche Änderungen der Index-Methode vor, und fügen Pagingschaltflächen zur Index-Ansicht hinzu. Die folgende Abbildung zeigt die Pagingschaltflächen.

Students index page with paging links

Erstellen Sie PaginatedList.cs im Projektordner. Ersetzen Sie den Vorlagencode dann durch den folgenden Code.

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);
        }
    }
}

Die CreateAsync-Methode in diesem Code akzeptiert die Seitengröße und die Seitenzahl und wendet die entsprechenden Skip- und Take-Anweisungen auf IQueryable an. Wenn ToListAsync auf IQueryable aufgerufen wird, wird eine Liste zurückgegeben, die nur die angeforderte Seite enthält. Die Eigenschaften HasPreviousPage und HasNextPage dienen zum Aktivieren oder Deaktivieren der Pagingschaltflächen Zurück und Weiter.

Ein CreateAsync-Methode wird anstelle eines Konstruktors verwendet, um das PaginatedList<T>-Objekt zu erstellen, da die Konstruktoren keinen asynchronen Code ausführen können.

Hinzufügen von Paging zur „Index“-Methode

Ersetzen Sie in StudentsController.cs die Index-Methode durch folgenden Code.

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));
}

Dieser Code fügt ein Parameter für die Seitenanzahl, die aktuelle Sortierreihenfolge und den aktuellen Filter zur Methodensignatur hinzu.

public async Task<IActionResult> Index(
    string sortOrder,
    string currentFilter,
    string searchString,
    int? pageNumber)

Wenn die Seite zum ersten Mal angezeigt wird oder wenn der Benutzer nicht auf einen Paging- oder Sortierlink geklickt hat, werden alle Parameter gleich 0 (null) sein. Wenn auf ein Paginglink geklickt wird, enthält die Seitenvariable die anzuzeigende Seitenzahl.

Das ViewData-Element „CurrentSort“ stellt die aktuelle Sortierreihenfolge für die Ansicht bereit. Diese Sortierreihenfolge muss in den Paginglinks enthalten sein, damit sie beim Pagingvorgang identisch bleibt.

Das ViewData-Element „CurrentFilter“ stellt der Ansicht die aktuelle Filterzeichenfolge zur Verfügung. Dieser Wert muss in den Paginglinks enthalten sein, damit die Filtereinstellungen während des Pagingvorgangs beibehalten werden, und er muss im Textfeld wiederhergestellt werden, wenn die Seite erneut angezeigt wird.

Wenn die Suchzeichenfolge während des Pagingvorgangs geändert wird, muss die Seite auf 1 zurückgesetzt werden, da der neue Filter andere Daten anzeigen kann. Die Suchzeichenfolge wird geändert, wenn ein Wert in das Textfeld eingegeben und auf die Schaltfläche „Senden“ geklickt wird. In diesem Fall ist der searchString-Parameter nicht gleich 0 (null).

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

Am Ende der Index-Methode konvertiert die PaginatedList.CreateAsync-Methode die Abfrage der Studentendaten in eine einzelne Seite in einem Sammlungstyp, der Pagingvorgänge unterstützt. Diese einzelnen Seite mit Studentendaten wird dann an die Ansicht übergeben.

return View(await PaginatedList<Student>.CreateAsync(students.AsNoTracking(), pageNumber ?? 1, pageSize));

Die PaginatedList.CreateAsync-Methode nimmt eine Seitenanzahl. Die zwei Fragezeichen stellen den Nullzusammensetzungsoperator dar. Der Nullzusammensetzungsoperator definiert einen Standardwert für einen Nullable-Typ. Der (pageNumber ?? 1)-Ausdruck bedeutet, dass pageNumber zurückgegeben wird, wenn dies über einen Wert verfügt, oder 1, wenn pageNumber gleich 0 (null) ist.

Ersetzen Sie in Views/Students/Index.cshtml den vorhandenen Code durch folgenden Code. Die Änderungen werden hervorgehoben.

@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>

Die @model-Anweisung am oberen Rand der Seite gibt an, dass die Ansicht nun ein PaginatedList<T>-Objekt anstelle eines List<T>-Objekts aufruft.

Die Spaltenüberschriftenlinks verwenden die Abfragezeichenfolge, um die aktuelle Suchzeichenfolge an den Controller zu übergeben, damit Benutzer Filterergebnisse sortieren können:

<a asp-action="Index" asp-route-sortOrder="@ViewData["DateSortParm"]" asp-route-currentFilter ="@ViewData["CurrentFilter"]">Enrollment Date</a>

Die Pagingschaltflächen werden durch Taghilfsprogramme angezeigt:

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

Führen Sie die Anwendung aus. Wechseln Sie zur Studentenseite.

Students index page with paging links

Klicken Sie auf die Paginglinks in verschiedenen Sortierreihenfolgen, um sicherzustellen, dass die Paging funktioniert. Geben Sie dann eine Suchzeichenfolge ein. Probieren Sie Paging erneut aus, um sicherzustellen, dass sie auch mit Sortier- und Filtervorgängen ordnungsgemäß funktioniert.

Erstellen einer Infoseite

Auf der Infoseite der Contoso University wird angezeigt, wie viele Studenten sich an welchem Datum angemeldet haben. Das erfordert Gruppieren und einfache Berechnungen dieser Gruppen. Um dies zu erreichen, ist Folgendes erforderlich:

  • Erstellen Sie eine Ansichtsmodellklasse für die Daten, die Sie an die Ansicht übergeben müssen.
  • Erstellen Sie die About-Methode im Home-Controller.
  • Erstellen Sie die Ansicht „Info“.

Erstellen des Ansichtsmodells

Erstellen Sie im Ordner Models (Modelle) den Ordner SchoolViewModels.

Fügen Sie im neuen Ordner die Klassendatei EnrollmentDateGroup.cs hinzu. Ersetzen Sie den Vorlagencode durch den folgenden Code:

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; }
    }
}

Ändern des Home-Controllers

Fügen Sie die folgenden using-Anweisungen am Anfang der Datei HomeController.cs ein:

using Microsoft.EntityFrameworkCore;
using ContosoUniversity.Data;
using ContosoUniversity.Models.SchoolViewModels;
using Microsoft.Extensions.Logging;

Fügen Sie eine Klassenvariable für den Datenbankkontext hinzu, unmittelbar nachdem Sie die geschweifte Klammer für die Klasse geöffnet haben. Rufen Sie eine Instanz des Kontexts von ASP.NET Core DI auf:

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly SchoolContext _context;

    public HomeController(ILogger<HomeController> logger, SchoolContext context)
    {
        _logger = logger;
        _context = context;
    }

Fügen Sie eine About-Methode mit dem folgenden Code hinzu:

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());
}

Die LINQ-Anweisung gruppiert die Studentenentitäten nach Anmeldedatum, berechnet die Anzahl der Entitäten in jeder Gruppe und speichert die Ergebnisse in einer Sammlung von EnrollmentDateGroup-Ansichtsmodellobjekten.

Erstellen der Ansicht „Info“

Fügen Sie eine Datei Views/Home/About.cshtml mit dem folgenden Code hinzu:

@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>

Führen Sie die Anwendung aus, und wechseln Sie zur Infoseite. Die Anzahl der Studenten für die jeweiligen Anmeldedatumswerte wird in einer Tabelle angezeigt.

Abrufen des Codes

Download or view the completed app (Herunterladen oder anzeigen der vollständigen App).

Nächste Schritte

In diesem Tutorial:

  • Spaltensortierungslinks wurden hinzugefügt
  • Ein Suchfeld wurde hinzugefügt
  • Paging wurde dem „Students“-Index hinzugefügt
  • Paging wurde der „Index“-Methode hinzugefügt
  • Paginglinks wurden hinzugefügt
  • Infoseite wurde erstellt

Fahren Sie mit dem nächsten Tutorial fort, um zu erfahren, wie Sie mithilfe von Migrationen Datenmodelländerungen verarbeiten.