Tutorial: Lesen verwandter Daten: ASP.NET MVC mit EF Core

Im vorherigen Tutorial haben Sie das Schuldatenmodell abgeschlossen. In diesem Tutorial lesen Sie verwandte Daten und zeigen sie an – d.h., die Daten, die Entity Framework in Navigationseigenschaften lädt.

Die folgenden Abbildungen zeigen die Seiten, mit den Sie arbeiten werden.

Courses Index page

Instructors Index page

In diesem Tutorial:

  • Erfahren Sie, wie verwandte Daten geladen werden.
  • Erstellen Sie eine Seite „Kurse“
  • Erstellen Sie eine Seite „Instructors“
  • Erfahren Sie mehr über das explizite Laden

Voraussetzungen

Es gibt mehrere Möglichkeiten, mit denen Software für objektrelationales Mapping (ORM), z.B. Entity Framework, verwandte Daten in die Navigationseigenschaften einer Entität laden kann:

  • Eager Loading: Wenn die Entität gelesen wird, werden zugehörige Daten zusammen mit ihr abgerufen. Dies führt normalerweise zu einer einzelnen Joinabfrage, die alle Daten abruft, die erforderlich sind. Sie geben Eager Loading in Entity Framework Core mit den Include- und ThenInclude-Methoden an.

    Eager loading example

    Sie können einige der Daten in separaten Abfragen abrufen und EF „korrigiert“ die Navigationseigenschaften. D.h., dass EF automatisch die separat abgerufenen Entitäten in die Navigationseigenschaften der zuvor abgerufenen Entitäten hinzufügt. Für die Abfrage, die verwandte Daten abruft, können Sie die Load-Methode verwenden, anstelle einer Methode, die eine Liste oder ein Objekt wie ToList oder Single zurückgibt.

    Separate queries example

  • Explizites Laden: Wenn die Entität zum ersten Mal gelesen wird, werden zugehörige Daten nicht abgerufen. Sie schreiben Code, der die verwandten Daten abruft, wenn erforderlich. So wie im Fall von Eager Loading mit separaten Abfragen führt explizites Laden zu mehreren Abfragen, die an die Datenbank gesendet werden. Der Unterschied liegt darin, dass beim expliziten Laden der Code die zu ladenden Navigationseigenschaften angibt. In Entity Framework Core 1.1 können Sie die Load-Methode verwenden, um das explizite Laden auszuführen. Beispiel:

    Explicit loading example

  • Lazy Loading: Wenn die Entität zum ersten Mal gelesen wird, werden zugehörige Daten nicht abgerufen. Wenn Sie jedoch zum ersten Mal versuchen, auf eine Navigationseigenschaft zuzugreifen, werden die für diese Navigationseigenschaft erforderlichen Daten automatisch abgerufen. Jedes Mal, wenn Sie zum ersten mal versuchen, Daten von einer Navigationseigenschaft abzurufen, wird eine Anfrage an die Datenbank gesendet. Entity Framework Core 1.0 unterstützt das Lazy Loading nicht.

Überlegungen zur Leistung

Wenn Sie wissen, dass Sie für jede abgerufene Entität verwandte Daten benötigen, bietet Eager Loading häufig die beste Leistung, da eine einzelne Abfrage, die an die Datenbank gesendet wird, in der Regel effizienter ist als separate Abfragen für jede abgerufene Entität. Nehmen wir beispielsweise an, dass jede Abteilung zehn verwandte Kurse hat. Eager Loading aller verwandter Daten würde zu nur einer einzelnen (Join)-Abfrage und einem einzelnen Roundtrip zur Datenbank führen. Eine separate Abfrage für Kurse jeder Abteilung würde zu elf Roundtrips zur Datenbank führen. Die zusätzlichen Roundtrips zur Datenbank beeinträchtigen die Leistung besonders bei hoher Latenz.

Andererseits sind separate Abfragen in einigen Szenarios jedoch effizienter. Eager Loading aller verwandter Daten in einer Abfrage könnte eine sehr komplexe Verknüpfung generieren, die der SQL Server nicht effizient verarbeiten kann. Oder angenommen, Sie benötigen nur Zugriff auf Navigationseigenschaften einer Entität, um auf eine Teilmenge der Reihe von Entitäten, die Sie verarbeiten, zuzugreifen. In diesem Fall können separate Abfragen möglicherweise besser ausgeführt werden, da Eager Loading aller Elemente im Vorfeld mehr Daten als benötigt abrufen würde. Wenn die Leistung wichtig ist, empfiehlt es sich, die Leistung mit beiden Möglichkeiten zu testen, um die beste Wahl treffen zu können.

Erstellen Sie eine Seite „Kurse“

Die Entität Course enthält eine Navigationseigenschaft, die die Department-Entität der Abteilung enthält, der der Kurs zugewiesen ist. Sie benötigen die Eigenschaft Name der Entität Department in der Navigationseigenschaft Course.Department, um den Namen der zugewiesenen Abteilung in einer Kursliste anzuzeigen.

Erstellen Sie einen Controller mit dem Namen CoursesController für den Entitätstyp Course. Verwenden Sie dieselben Optionen für den MVC-Controller mit Ansichten unter Verwendung des Entity Framework-Scaffolders, die Sie zuvor für StudentsController verwendet haben. Dies wird auf der folgenden Abbildung veranschaulicht:

Add Courses controller

Öffnen Sie CoursesController.cs, und untersuchen Sie die Index-Methode. Der automatische Gerüstbau hat mithilfe der Include-Methode ein Eager Loading für die Department-Navigationseigenschaft angegeben.

Ersetzen Sie die Index-Methode durch den folgenden Code, der einen geeigneteren Namen für IQueryable verwendet, der Kursentitäten (courses anstelle von schoolContext) zurückgibt:

public async Task<IActionResult> Index()
{
    var courses = _context.Courses
        .Include(c => c.Department)
        .AsNoTracking();
    return View(await courses.ToListAsync());
}

Öffnen Sie Views/Courses/Index.cshtml, und ersetzen Sie den Vorlagencode durch folgenden Code. Die Änderungen werden hervorgehoben:

@model IEnumerable<ContosoUniversity.Models.Course>

@{
    ViewData["Title"] = "Courses";
}

<h2>Courses</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>
                @Html.DisplayNameFor(model => model.CourseID)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Title)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Credits)
            </th>
            <th>
                @Html.DisplayNameFor(model => model.Department)
            </th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.CourseID)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Credits)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Department.Name)
                </td>
                <td>
                    <a asp-action="Edit" asp-route-id="@item.CourseID">Edit</a> |
                    <a asp-action="Details" asp-route-id="@item.CourseID">Details</a> |
                    <a asp-action="Delete" asp-route-id="@item.CourseID">Delete</a>
                </td>
            </tr>
        }
    </tbody>
</table>

Sie haben die folgenden Änderungen am eingerüsteten Code vorgenommen:

  • Die Überschrift wurde von Index in Courses geändert.

  • Die Spalte Anzahl wurde hinzugefügt. Sie zeigt den CourseID-Eigenschaftswert an. Primärschlüssel werden nicht standardmäßig eingerüstet, da sie normalerweise ohne Bedeutung für Endbenutzer sind. Allerdings ist in diesem Fall der Primärschlüssel sinnvoll, und Sie möchten ihn anzeigen.

  • Die Spalte Abteilung wurde geändert, sodass sie jetzt den Namen der Abteilung anzeigt. Der Code zeigt die Name-Eigenschaft der Department-Entität an, die in die Department-Navigationseigenschaft geladen wird:

    @Html.DisplayFor(modelItem => item.Department.Name)
    

Führen Sie die Anwendung aus. Wählen Sie die Registerkarte Kurse aus, um die Liste mit den Abteilungsnamen anzuzeigen.

Courses Index page

Erstellen Sie eine Seite „Instructors“

In diesem Abschnitt müssen Sie einen Controller und eine Ansicht für die Entität Instructor erstellen, um die Seite „Instructors“ anzuzeigen:

Instructors Index page

Auf dieser Seite werden verwandte Daten auf folgende Weise gelesen und angezeigt:

  • Die Liste der Lehrkräfte zeigt zugehörige Daten aus der Entität OfficeAssignment an. Die Instructor- und OfficeAssignment-Entitäten stehen in einer 1:0..1-Beziehung zueinander. Für die OfficeAssignment-Entitäten verwenden Sie das Eager Loading. Wie zuvor erläutert, ist Eager Loading in der Regel effizienter, wenn Sie die verwandten Daten für alle abgerufenen Zeilen der primären Tabelle benötigen. In diesem Fall sollten Sie die Office-Anweisungen für alle angezeigten Dozenten anzeigen.

  • Wenn der Benutzer einen Dozenten auswählt, werden zugehörige Course-Entitäten angezeigt. Die Instructor- und Course-Entitäten stehen in einer m:n-Beziehung zueinander. Für die Course-Entitäten und die zugehörigen Department-Entitäten verwenden Sie das Eager Loading. In diesem Fall können separate Abfrage effizienter sein, da nur Kurse für den ausgewählten Dozenten benötigt werden. Dieses Beispiel zeigt jedoch, wie Eager Loading für Navigationseigenschaften in Entitäten in den Navigationseigenschaften verwendet wird.

  • Wenn der Benutzer einen Kurs auswählt, werden zugehörige Daten aus der Entitätenmenge Enrollments angezeigt. Die Course- und Enrollment-Entitäten stehen in einer 1:n-Beziehung zueinander. Für die Enrollment-Entitäten und die zugehörigen Student-Entitäten verwenden Sie jeweils separate Abfragen.

Erstellen eines Ansichtsmodells für die Indexansicht „Dozenten“

Die Dozentenseite zeigt Daten aus drei verschiedenen Tabellen. Aus diesem Grund erstellen Sie ein Ansichtsmodell, das drei Eigenschaften enthält. Jede enthält Daten für eine der Tabellen.

Erstellen Sie im Ordner SchoolViewModelsInstructorIndexData.cs, und ersetzen Sie den bestehenden Code durch den folgenden Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace ContosoUniversity.Models.SchoolViewModels
{
    public class InstructorIndexData
    {
        public IEnumerable<Instructor> Instructors { get; set; }
        public IEnumerable<Course> Courses { get; set; }
        public IEnumerable<Enrollment> Enrollments { get; set; }
    }
}

Erstellen der Dozentencontroller und -ansichten

Erstellen Sie einen Dozentencontroller mit EF-Lese-/Schreibaktionen, wie in der folgenden Abbildung gezeigt:

Add Instructors controller

Öffnen Sie InstructorsController.cs. Fügen Sie eine Using-Anweisung für den Namespace ViewModels hinzu:

using ContosoUniversity.Models.SchoolViewModels;

Ersetzen Sie die Indexmethode durch den folgenden Code, um Eager Loading verwandter Daten durchzuführen und ihn in das Ansichtsmodell einzufügen.

public async Task<IActionResult> Index(int? id, int? courseID)
{
    var viewModel = new InstructorIndexData();
    viewModel.Instructors = await _context.Instructors
          .Include(i => i.OfficeAssignment)
          .Include(i => i.CourseAssignments)
            .ThenInclude(i => i.Course)
                .ThenInclude(i => i.Enrollments)
                    .ThenInclude(i => i.Student)
          .Include(i => i.CourseAssignments)
            .ThenInclude(i => i.Course)
                .ThenInclude(i => i.Department)
          .AsNoTracking()
          .OrderBy(i => i.LastName)
          .ToListAsync();
    
    if (id != null)
    {
        ViewData["InstructorID"] = id.Value;
        Instructor instructor = viewModel.Instructors.Where(
            i => i.ID == id.Value).Single();
        viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
    }

    if (courseID != null)
    {
        ViewData["CourseID"] = courseID.Value;
        viewModel.Enrollments = viewModel.Courses.Where(
            x => x.CourseID == courseID).Single().Enrollments;
    }

    return View(viewModel);
}

Die Methode akzeptiert optionale Routendaten (id) und einen Abfragezeichenfolgenparameter (courseID), die die ID-Werte des ausgewählten Dozenten und Kurses bereitstellen. Die Parameter werden durch die Auswählen-Links auf der Seite bereitgestellt.

Der Code erstellt zuerst eine Instanz des Ansichtsmodells und fügt die Dozentenliste ein. Der Code gibt Eager Loading für die Instructor.OfficeAssignment- und Instructor.CourseAssignments-Navigationseigenschaften an. Innerhalb der CourseAssignments-Eigenschaft wird die Course-Eigenschaft geladen, und innerhalb dieser werden die Enrollments- und Department-Eigenschaften geladen, und innerhalb jeder Enrollment-Entität wird die Student-Eigenschaft geladen.

viewModel.Instructors = await _context.Instructors
      .Include(i => i.OfficeAssignment)
      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Enrollments)
                .ThenInclude(i => i.Student)
      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Department)
      .AsNoTracking()
      .OrderBy(i => i.LastName)
      .ToListAsync();

Da für die Ansicht immer die Entität OfficeAssignment erforderlich ist, ist es effizienter, sie in derselben Abfrage abzurufen. Kursentitäten sind erforderlich, wenn auf der Webseite ein Dozent ausgewählt ist. Somit ist eine einzelne Abfrage nur besser als mehrere Abfragen, wenn die Seite häufiger mit einem ausgewählten Kurs als ohne angezeigt wird.

Der Code wiederholt CourseAssignments und Course, da Sie zwei Eigenschaften aus Course benötigen. Die erste Zeichenfolge der ThenInclude-Abrufe erhält CourseAssignment.Course, Course.Enrollments und Enrollment.Student.

Hier finden Sie weitere Informationen zum Einschließen mehrerer Ebenen zugehöriger Daten.

viewModel.Instructors = await _context.Instructors
      .Include(i => i.OfficeAssignment)
      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Enrollments)
                .ThenInclude(i => i.Student)
      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Department)
      .AsNoTracking()
      .OrderBy(i => i.LastName)
      .ToListAsync();

An diesem Punkt im Code wäre eine andere ThenInclude für Navigationseigenschaften von Student, die Sie nicht benötigen. Aber ein Aufruf von Include beginnt mit Instructor-Eigenschaften neu. Daher müssen Sie den Vorgang erneut durchlaufen und Course.Department anstelle von Course.Enrollments angeben.

viewModel.Instructors = await _context.Instructors
      .Include(i => i.OfficeAssignment)
      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Enrollments)
                .ThenInclude(i => i.Student)
      .Include(i => i.CourseAssignments)
        .ThenInclude(i => i.Course)
            .ThenInclude(i => i.Department)
      .AsNoTracking()
      .OrderBy(i => i.LastName)
      .ToListAsync();

Der folgende Code wird ausgeführt, wenn ein Dozent ausgewählt wurde. Der ausgewählte Dozent wird aus der Liste der Dozenten im Ansichtsmodell abgerufen. Die Eigenschaft Courses des Ansichtsmodells wird dann mit den Course-Entitäten aus der Navigationseigenschaft CourseAssignments dieser Lehrkraft geladen.

if (id != null)
{
    ViewData["InstructorID"] = id.Value;
    Instructor instructor = viewModel.Instructors.Where(
        i => i.ID == id.Value).Single();
    viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
}

Die Where-Methode gibt eine Sammlung zurück. Aber in diesem Fall resultieren die an diese Methode übergebenen Kriterien nur in einer einzigen zurückgegebenen Instructor-Entität. Die Methode Single konvertiert die Sammlung in eine einzelne Instructor-Entität, die Ihnen Zugriff auf die CourseAssignments-Eigenschaft dieser Entität erteilt. Die CourseAssignments-Eigenschaft enthält CourseAssignment-Entitäten, aus der Sie nur die verwandten Course-Entitäten benötigen.

Verwenden Sie die Single-Methode für eine Sammlung, wenn Sie wissen, dass die Sammlung nur über ein Element verfügt. Die Methode Single löst eine Ausnahme aus, wenn die Sammlung, an die Inhalt übergeben wurde, leer ist, oder wenn mehr als ein Element vorhanden ist. Eine Alternative ist SingleOrDefault, womit ein Standardwert (in diesem Fall null) zurückgegeben wird, wenn die Sammlung leer ist. In diesem Fall würde jedoch trotzdem eine Ausnahme ausgelöst werden (da versucht wird, eine Courses-Eigenschaft auf einem Null-Verweis zu finden). Die Ausnahmemeldung würde die Ursache des Problems weniger deutlich angeben. Wenn Sie die Single-Methode aufrufen, können Sie auch in die Where-Bedingung übergehen, anstatt die Where-Methode separat aufzurufen:

.Single(i => i.ID == id.Value)

anstelle von:

.Where(i => i.ID == id.Value).Single()

Wenn ein Kurs ausgewählt wurde, wird der ausgewählte Kurs aus der Kursliste im Ansichtsmodell abgerufen. Die Enrollments-Eigenschaft des Ansichtsmodells wird dann mit den Registrierungsentitäten aus der Enrollments-Navigationseigenschaft dieses Kurses geladen.

if (courseID != null)
{
    ViewData["CourseID"] = courseID.Value;
    viewModel.Enrollments = viewModel.Courses.Where(
        x => x.CourseID == courseID).Single().Enrollments;
}

Nachverfolgung und keine Nachverfolgung im Vergleich

Abfragen ohne Nachverfolgung sind nützlich, wenn die Ergebnisse in einem schreibgeschützten Szenario verwendet werden. Sie werden in der Regel schneller ausgeführt, da keine Informationen für die Änderungsnachverfolgung eingerichtet werden müssen. Wenn die aus der Datenbank abgerufenen Entitäten nicht aktualisiert werden müssen, ist eine Abfrage ohne Nachverfolgung wahrscheinlich besser als eine Abfrage mit Nachverfolgung.

In einigen Fällen ist eine Abfrage mit Nachverfolgung effizienter als eine Abfrage ohne Nachverfolgung. Weitere Informationen finden Sie unter Abfragen mit Nachverfolgung und Abfragen ohne Nachverfolgung im Vergleich.

Ändern der Dozentenindexansicht

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

@model ContosoUniversity.Models.SchoolViewModels.InstructorIndexData

@{
    ViewData["Title"] = "Instructors";
}

<h2>Instructors</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>Last Name</th>
            <th>First Name</th>
            <th>Hire Date</th>
            <th>Office</th>
            <th>Courses</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Instructors)
        {
            string selectedRow = "";
            if (item.ID == (int?)ViewData["InstructorID"])
            {
                selectedRow = "table-success";
            }
            <tr class="@selectedRow">
                <td>
                    @Html.DisplayFor(modelItem => item.LastName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.FirstMidName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.HireDate)
                </td>
                <td>
                    @if (item.OfficeAssignment != null)
                    {
                        @item.OfficeAssignment.Location
                    }
                </td>
                <td>
                    @foreach (var course in item.CourseAssignments)
                    {
                        @course.Course.CourseID @course.Course.Title <br />
                    }
                </td>
                <td>
                    <a asp-action="Index" asp-route-id="@item.ID">Select</a> |
                    <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>
@model ContosoUniversity.Models.SchoolViewModels.InstructorIndexData

@{
    ViewData["Title"] = "Instructors";
}

<h2>Instructors</h2>

<p>
    <a asp-action="Create">Create New</a>
</p>
<table class="table">
    <thead>
        <tr>
            <th>Last Name</th>
            <th>First Name</th>
            <th>Hire Date</th>
            <th>Office</th>
            <th>Courses</th>
            <th></th>
        </tr>
    </thead>
    <tbody>
        @foreach (var item in Model.Instructors)
        {
            string selectedRow = "";
            if (item.ID == (int?)ViewData["InstructorID"])
            {
                selectedRow = "success";
            }
            <tr class="@selectedRow">
                <td>
                    @Html.DisplayFor(modelItem => item.LastName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.FirstMidName)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.HireDate)
                </td>
                <td>
                    @if (item.OfficeAssignment != null)
                    {
                        @item.OfficeAssignment.Location
                    }
                </td>
                <td>
                    @foreach (var course in item.CourseAssignments)
                    {
                        @course.Course.CourseID @course.Course.Title <br />
                    }
                </td>
                <td>
                    <a asp-action="Index" asp-route-id="@item.ID">Select</a> |
                    <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>

Sie haben die folgenden Änderungen am bestehenden Code vorgenommen:

  • Die Modellklasse wurde zu InstructorIndexData geändert.

  • Der Seitenname wurde von Index in Dozenten geändert.

  • Es wurde eine Office-Spalte hinzugefügt, die item.OfficeAssignment.Location nur anzeigt, wenn item.OfficeAssignment nicht gleich null ist. (Da dies eine 1:0..1-Beziehung ist, gibt es möglicherweise keine verwandte OfficeAssignment-Entität.)

    @if (item.OfficeAssignment != null)
    {
        @item.OfficeAssignment.Location
    }
    
  • Es wurde eine Kurse-Spalte hinzugefügt, die die Kurse eines jeden Dozenten anzeigt. Weitere Informationen finden Sie im Abschnitt Explicit line transition (Expliziter Zeilenübergang) des Razor-Syntaxartikels.

  • Es wurde Code hinzugefügt, der dem tr-Element des ausgewählten Dozenten bedingt eine Bootstrap-CSS-Klasse hinzufügt. Diese Klasse legt eine Hintergrundfarbe für die ausgewählte Zeile fest.

  • Es wurde ein neuer Link mit der Bezeichnung Auswählen unmittelbar vor den anderen Links in jeder Zeile hinzugefügt. Dies führt dazu, dass die ID des ausgewählten Dozenten an die Index-Methode gesendet wird.

    <a asp-action="Index" asp-route-id="@item.ID">Select</a> |
    

Führen Sie die Anwendung aus. Klicken Sie auf die Registerkarte Dozenten. Die Seite zeigt die Eigenschaft für den Standort verwandter OfficeAssignment-Entitäten und eine leere Zelle an, wenn keine verwandte OfficeAssignment-Entität vorhanden ist.

Instructors Index page nothing selected

Fügen Sie in der Datei Views/Instructors/Index.cshtml nach dem Schließen des Tabellenelements (am Ende der Datei) den folgenden Code hinzu. Dieser Code zeigt eine Liste der Kurse an, die im Zusammenhang mit einem Dozenten stehen, wenn ein Dozent ausgewählt wird.


@if (Model.Courses != null)
{
    <h3>Courses Taught by Selected Instructor</h3>
    <table class="table">
        <tr>
            <th></th>
            <th>Number</th>
            <th>Title</th>
            <th>Department</th>
        </tr>

        @foreach (var item in Model.Courses)
        {
            string selectedRow = "";
            if (item.CourseID == (int?)ViewData["CourseID"])
            {
                selectedRow = "success";
            }
            <tr class="@selectedRow">
                <td>
                    @Html.ActionLink("Select", "Index", new { courseID = item.CourseID })
                </td>
                <td>
                    @item.CourseID
                </td>
                <td>
                    @item.Title
                </td>
                <td>
                    @item.Department.Name
                </td>
            </tr>
        }

    </table>
}

Dieser Code liest die Courses-Eigenschaft des Ansichtsmodells, um eine Kursliste anzuzeigen. Er bietet außerdem einen Auswählen-Link, der die ID des ausgewählten Kurses an die Index-Aktionsmethode sendet.

Aktualisieren Sie die Seite. Wählen Sie einen Dozenten aus. Jetzt sehen Sie ein Raster, das die dem Dozenten zugewiesenen Kurse anzeigt. Sie sehen auch den Namen der zugewiesenen Abteilung für jeden Kurs.

Instructors Index page instructor selected

Fügen Sie den folgenden Code hinzu, nachdem Sie den Codeblock hinzugefügt haben. Dies zeigt eine Liste der Studenten an, die im Kurs registriert sind, wenn dieser Kurs ausgewählt ist.

@if (Model.Enrollments != null)
{
    <h3>
        Students Enrolled in Selected Course
    </h3>
    <table class="table">
        <tr>
            <th>Name</th>
            <th>Grade</th>
        </tr>
        @foreach (var item in Model.Enrollments)
        {
            <tr>
                <td>
                    @item.Student.FullName
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Grade)
                </td>
            </tr>
        }
    </table>
}

Dieser Code liest die Eigenschaft Enrollments des Ansichtsmodells, um eine Liste der in diesem Kurs registrierten Studierenden anzuzeigen.

Aktualisieren Sie die Seite erneut. Wählen Sie einen Dozenten aus. Wählen Sie dann einen Kurs aus, um die Liste der registrierten Studenten und deren Noten einzusehen.

Instructors Index page instructor and course selected

Informationen zum expliziten Laden

Wenn Sie die Liste der Kursleiter aus InstructorsController.cs abrufen, haben Sie Eager Loading für die CourseAssignments-Navigationseigenschaft angegeben.

Angenommen, Sie haben erwartet, dass Benutzer nur selten Registrierungen für einen ausgewählten Dozenten und Kurs angezeigt haben möchten. In diesem Fall möchten Sie die Registrierungsdaten möglicherweise nur laden, wenn diese angefordert werden. Ersetzen Sie die Methode Index durch folgenden Code, um ein Beispiel für Eager Loading zu sehen. Dieser Code entfernt das Eager Loading für Enrollments und lädt diese Eigenschaft explizit. Die Codeänderungen werden hervorgehoben.

public async Task<IActionResult> Index(int? id, int? courseID)
{
    var viewModel = new InstructorIndexData();
    viewModel.Instructors = await _context.Instructors
          .Include(i => i.OfficeAssignment)
          .Include(i => i.CourseAssignments)
            .ThenInclude(i => i.Course)
                .ThenInclude(i => i.Department)
          .OrderBy(i => i.LastName)
          .ToListAsync();

    if (id != null)
    {
        ViewData["InstructorID"] = id.Value;
        Instructor instructor = viewModel.Instructors.Where(
            i => i.ID == id.Value).Single();
        viewModel.Courses = instructor.CourseAssignments.Select(s => s.Course);
    }

    if (courseID != null)
    {
        ViewData["CourseID"] = courseID.Value;
        var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
        await _context.Entry(selectedCourse).Collection(x => x.Enrollments).LoadAsync();
        foreach (Enrollment enrollment in selectedCourse.Enrollments)
        {
            await _context.Entry(enrollment).Reference(x => x.Student).LoadAsync();
        }
        viewModel.Enrollments = selectedCourse.Enrollments;
    }

    return View(viewModel);
}

Der neue Code löscht die ThenInclude-Methodenaufrufe für Registrierungsdaten aus dem Code, der Instructor-Entitäten abruft. Außerdem wird AsNoTracking gelöscht. Wenn eine Lehrkraft und Kurs ausgewählt werden, ruft der hervorgehobene Code die Enrollment-Entitäten für den ausgewählten Kurs und die Student-Entitäten für jede Enrollment-Entität ab.

Führen Sie die Anwendung aus, navigieren Sie zur Dozentenindexseite, und Sie werden keinen Unterschied in der Anzeige auf der Seite bemerken, obwohl Sie die Abrufart für Daten geändert haben.

Abrufen des Codes

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

Nächste Schritte

In diesem Tutorial:

  • Haben Sie gelernt, wie verwandte Daten geladen werden
  • Wurde eine Seite „Kurse“ erstellt
  • Wurde eine Seite „Instructors“ erstellt
  • Haben Sie mehr über das explizite Laden erfahren

Fahren Sie mit dem nächsten Tutorial fort, um mehr über das Aktualisieren zugehöriger Daten zu erfahren.