Freigeben über


Lesen verwandter Daten mit dem Entity Framework in einer ASP.NET MVC-Anwendung (5 von 10)

von Tom Dykstra

Die Contoso University-Beispielwebanwendung veranschaulicht, wie Sie ASP.NET MVC 4-Anwendungen mithilfe von Entity Framework 5 Code First und Visual Studio 2012 erstellen. Informationen zu dieser Tutorialreihe finden Sie im ersten Tutorial der Reihe.

Hinweis

Wenn ein Problem auftritt, das nicht behoben werden kann, laden Sie das abgeschlossene Kapitel herunter , und versuchen Sie, das Problem zu reproduzieren. Im Allgemeinen können Sie die Lösung für das Problem finden, indem Sie Ihren Code mit dem abgeschlossenen Code vergleichen. Einige häufige Fehler und deren Behebung finden Sie unter Fehler und Problemumgehungen.

Im vorherigen Tutorial haben Sie das Schuldatenmodell abgeschlossen. In diesem Tutorial lesen und zeigen Sie verwandte Daten an, d. h. Daten, die vom Entity Framework in Navigationseigenschaften geladen werden.

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

Screenshot: Seite

Screenshot: Seite

Es gibt mehrere Möglichkeiten, wie das Entity Framework verwandte Daten in die Navigationseigenschaften einer Entität laden kann:

  • Lazy Loading (verzögertes Laden). Wenn die Entität zuerst gelesen wird, werden verwandte Daten nicht abgerufen. Wenn Sie jedoch zum ersten Mal versuchen, auf eine Navigationseigenschaft zuzugreifen, werden die für diese Navigationseigenschaft erforderlichen Daten automatisch abgerufen. Dies führt zu mehreren Abfragen, die an die Datenbank gesendet werden – eine für die Entität selbst und eine, die jedes Mal verknüpfte Daten für die Entität abgerufen werden müssen.

    Lazy_loading_example

  • Eager Loading (vorzeitiges Laden). Wenn die Entität gelesen wird, werden ihre verwandten Daten mit ihr abgerufen. Dies führt normalerweise zu einer einzelnen Joinabfrage, die alle Daten abruft, die erforderlich sind. Sie geben mit der Include -Methode "Eager Loading" an.

    Eager_loading_example

  • Explizites Laden. Dies ähnelt dem verzögerten Laden, mit der Ausnahme, dass Sie die zugehörigen Daten explizit im Code abrufen. Dies geschieht nicht automatisch, wenn Sie auf eine Navigationseigenschaft zugreifen. Sie laden verwandte Daten manuell, indem Sie den Objektstatus-Manager-Eintrag für eine Entität abrufen und die Collection.Load Methode für Sammlungen oder die Methode für Eigenschaften aufrufen, die Reference.Load eine einzelne Entität enthalten. (Wenn Sie im folgenden Beispiel die Administratornavigationseigenschaft laden möchten, ersetzen Collection(x => x.Courses) Sie durch Reference(x => x.Administrator).)

    Explicit_loading_example

Da die Eigenschaftswerte nicht sofort abgerufen werden, werden verzögertes Laden und explizites Laden auch als verzögertes Laden bezeichnet.

Im Allgemeinen, wenn Sie wissen, dass Sie verwandte Daten für jede abgerufene Entität benötigen, bietet das eifrige Laden 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 Sie beispielsweise in den obigen Beispielen an, dass jede Abteilung über zehn verwandte Kurse verfügt. Das Beispiel für das eifrige Laden würde nur zu einer einzelnen Abfrage (Join) und einem einzelnen Roundtrip zur Datenbank führen. Die Beispiele für verzögertes Laden und explizites Laden würden zu elf Abfragen und elf Roundtrips zur Datenbank führen. Die zusätzlichen Roundtrips zur Datenbank beeinträchtigen die Leistung besonders bei hoher Latenz.

Auf der anderen Seite ist in einigen Szenarien das lazy Laden effizienter. Beim eifrigen Laden wird möglicherweise eine sehr komplexe Verknüpfung generiert, die SQL Server nicht effizient verarbeiten kann. Oder wenn Sie nur für eine Teilmenge einer Gruppe von Entitäten, die Sie verarbeiten, auf die Navigationseigenschaften einer Entität zugreifen müssen, kann das verzögerte Laden besser funktionieren, da beim eifrigen Laden mehr Daten abgerufen werden, als Sie benötigen. Wenn die Leistung wichtig ist, empfiehlt es sich, die Leistung mit beiden Möglichkeiten zu testen, um die beste Wahl treffen zu können.

In der Regel verwenden Sie explizites Laden nur, wenn Sie das verzögerte Laden deaktiviert haben. Ein Szenario, in dem Sie das verzögerte Laden deaktivieren sollten, ist während der Serialisierung. Lazy loading and serialization funktionieren nicht gut, und wenn Sie nicht vorsichtig sind, können Sie am Ende deutlich mehr Daten abfragen, als Sie beabsichtigt haben, wenn das verzögerte Laden aktiviert ist. Die Serialisierung funktioniert in der Regel durch Zugriff auf jede Eigenschaft auf einem instance eines Typs. Der Eigenschaftenzugriff löst das verzögerte Laden aus, und diese lazy geladenen Entitäten werden serialisiert. Der Serialisierungsprozess greift dann auf jede Eigenschaft der lazy geladenen Entitäten zu, was möglicherweise zu noch mehr Verzögertem Laden und Serialisieren führt. Um diese Verkettungsreaktion zu verhindern, schalten Sie das verzögerte Laden aus, bevor Sie eine Entität serialisieren.

Die Datenbankkontextklasse führt standardmäßig verzögertes Laden aus. Es gibt zwei Möglichkeiten, das verzögerte Laden zu deaktivieren:

  • Lassen Sie bei bestimmten Navigationseigenschaften die virtual Schlüsselwort (keyword) weg, wenn Sie die Eigenschaft deklarieren.

  • Legen Sie für alle Navigationseigenschaften auf falsefestLazyLoadingEnabled. Beispielsweise können Sie den folgenden Code in den Konstruktor Ihrer Kontextklasse einfügen:

    this.Configuration.LazyLoadingEnabled = false;
    

Verzögertes Laden kann Code masken, der Leistungsprobleme verursacht. Code, der z. B. kein eifriges oder explizites Laden angibt, sondern eine große Menge von Entitäten verarbeitet und mehrere Navigationseigenschaften in jeder Iteration verwendet, kann sehr ineffizient sein (aufgrund vieler Roundtrips zur Datenbank). Eine Anwendung, die bei der Entwicklung mit einem lokalen SQL Server gut abschneidet, kann aufgrund der erhöhten Latenz und des verzögerten Ladens zu Azure SQL Datenbank zu Leistungsproblemen führen. Durch die Profilerstellung der Datenbankabfragen mit einer realistischen Testauslastung können Sie ermitteln, ob das verzögerte Laden angemessen ist. Weitere Informationen finden Sie unter Entmystifizieren von Entity Framework-Strategien: Laden verwandter Daten und Verwenden des Entity Frameworks zum Reduzieren der Netzwerklatenz auf SQL Azure.

Erstellen einer Kursindexseite mit Abteilungsname

Die Entität Course enthält eine Navigationseigenschaft, die die Department-Entität der Abteilung enthält, der der Kurs zugewiesen ist. Um den Namen der zugewiesenen Abteilung in einer Liste von Kursen anzuzeigen, müssen Sie die Name Eigenschaft von der Department Entität abrufen, die sich in der Course.Department Navigationseigenschaft befindet.

Erstellen Sie einen Controller namens CourseController für den Course Entitätstyp, und verwenden Sie die gleichen Optionen wie zuvor für den Student Controller, wie in der folgenden Abbildung gezeigt (außer, dass sich Ihre Kontextklasse im DAL-Namespace befindet, nicht im Models-Namespace):

Add_Controller_dialog_box_for_Course_controller

Öffnen Sie Controller\CourseController.cs , und sehen Sie sich die Methode an Index :

public ViewResult Index()
{
    var courses = db.Courses.Include(c => c.Department);
    return View(courses.ToList());
}

Der automatische Gerüstbau hat mithilfe der Include-Methode ein Eager Loading für die Department-Navigationseigenschaft angegeben.

Öffnen Sie Views\Course\Index.cshtml , und ersetzen Sie den vorhandenen Code durch den folgenden Code. Die Änderungen werden hervorgehoben:

@model IEnumerable<ContosoUniversity.Models.Course>

@{
    ViewBag.Title = "Courses";
}

<h2>Courses</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th></th>
        <th>Number</th>
        <th>Title</th>
        <th>Credits</th>
        <th>Department</th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.CourseID }) |
            @Html.ActionLink("Details", "Details", new { id=item.CourseID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.CourseID })
        </td>
        <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>
    </tr>
}
</table>

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

  • Die Überschrift wurde von Index in Courses geändert.
  • Die Zeilenlinks wurden nach links verschoben.
  • Unter der Überschrift Zahl wurde eine Spalte hinzugefügt, die den CourseID Eigenschaftswert anzeigt. (Primärschlüssel sind standardmäßig nicht gerüstet, da sie normalerweise für Endbenutzer bedeutungslos sind. In diesem Fall ist der Primärschlüssel jedoch sinnvoll, und Sie möchten ihn anzeigen.)
  • Die letzte Spaltenüberschrift wurde von DepartmentID (der Name des Fremdschlüssels der Entität) in DepartmentDepartment geändert.

Beachten Sie, dass der Gerüstcode für die letzte Spalte die Name Eigenschaft der Entität anzeigt, die Department in die Department Navigationseigenschaft geladen wird:

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

Führen Sie die Seite aus (wählen Sie auf der Startseite der Contoso University die Registerkarte Kurse aus), um die Liste mit den Abteilungsnamen anzuzeigen.

Courses_index_page_with_department_names

Erstellen einer Kursleiterindexseite mit Kursen und Registrierungen

In diesem Abschnitt erstellen Sie einen Controller und eine Ansicht für die Instructor Entität, um die Seite Kursleiterindex anzuzeigen:

Screenshot: Seite

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 kann das faule Laden effizienter sein, da Sie Kurse nur für den ausgewählten Kursleiter benötigen. 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. Sie fügen explizites Laden für Enrollment Entitäten und die zugehörigen Student Entitäten hinzu. (Explizites Laden ist nicht erforderlich, da das verzögerte Laden aktiviert ist, aber dies zeigt, wie explizit geladen wird.)

Erstellen eines Ansichtsmodells für die Kursleiterindexansicht

Auf der Seite Kursleiterindex werden drei verschiedene Tabellen angezeigt. 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 ViewModelsInstructorIndexData.cs , und ersetzen Sie den vorhandenen Code durch den folgenden Code:

using System.Collections.Generic;
using ContosoUniversity.Models;

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

Hinzufügen eines Stils für ausgewählte Zeilen

Um ausgewählte Zeilen zu markieren, benötigen Sie eine andere Hintergrundfarbe. Um eine Formatvorlage für diese Benutzeroberfläche bereitzustellen, fügen Sie den folgenden hervorgehobenen Code zum Abschnitt /* info and errors */ in Content\Site.css hinzu, wie unten gezeigt:

/* info and errors */
.selectedrow 
{ 
    background-color: #a4d4e6; 
}
.message-info {
    border: 1px solid;
    clear: both;
    padding: 10px 20px;
}

Erstellen des Dozentencontrollers und der Ansichten

Erstellen Sie einen InstructorController Controller, wie in der folgenden Abbildung gezeigt:

Add_Controller_dialog_box_for_Instructor_controller

Öffnen Sie Controllers\InstructorController.cs , und fügen Sie eine using Anweisung für den ViewModels Namespace hinzu:

using ContosoUniversity.ViewModels;

Der gerüstete Code in der Index -Methode gibt eager loading nur für die OfficeAssignment Navigationseigenschaft an:

public ViewResult Index()
{
    var instructors = db.Instructors.Include(i => i.OfficeAssignment);
    return View(instructors.ToList());
}

Ersetzen Sie die Index -Methode durch den folgenden Code, um zusätzliche verwandte Daten zu laden und im Ansichtsmodell zu platzieren:

public ActionResult Index(int? id, int? courseID)
{
    var viewModel = new InstructorIndexData();
    viewModel.Instructors = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.Courses.Select(c => c.Department))
        .OrderBy(i => i.LastName);

    if (id != null)
    {
        ViewBag.InstructorID = id.Value;
        viewModel.Courses = viewModel.Instructors.Where(
            i => i.InstructorID == id.Value).Single().Courses;
    }

    if (courseID != null)
    {
        ViewBag.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 Kursleiters und des ausgewählten Kurses angeben, und übergibt alle erforderlichen Daten an die Ansicht. Die Parameter werden durch die Auswählen-Links auf der Seite bereitgestellt.

Tipp

Routendaten

Routendaten sind Daten, die der Modellbinder in einem URL-Segment gefunden hat, das in der Routingtabelle angegeben ist. Die Standardroute gibt beispielsweise die Segmente , actionund id ancontroller:

routes.MapRoute(  
 name: "Default",  
 url: "{controller}/{action}/{id}",  
 defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }  
);

In der folgenden URL wird Instructor die Standardroute als controller, Index als action und 1 als zugeordnet id. Dabei handelt es sich um Routendatenwerte.

http://localhost:1230/Instructor/Index/1?courseID=2021

"?courseID=2021" ist ein Abfragezeichenfolgenwert. Die Modellbindung funktioniert auch, wenn Sie den id als Abfragezeichenfolgenwert übergeben:

http://localhost:1230/Instructor/Index?id=1&CourseID=2021

Die URLs werden von ActionLink Anweisungen in der Razor-Ansicht erstellt. Im folgenden Code stimmt der id Parameter mit der Standardroute überein, sodass id den Routendaten hinzugefügt wird.

@Html.ActionLink("Select", "Index", new { id = item.PersonID  })

Im folgenden Code courseID stimmt nicht mit einem Parameter in der Standardroute überein, daher wird er als Abfragezeichenfolge hinzugefügt.

@Html.ActionLink("Select", "Index", new { courseID = item.CourseID })

Der Code erstellt zuerst eine Instanz des Ansichtsmodells und fügt die Dozentenliste ein. Der Code gibt eager loading für die Instructor.OfficeAssignment Navigationseigenschaft und an Instructor.Courses .

var viewModel = new InstructorIndexData();
viewModel.Instructors = db.Instructors
    .Include(i => i.OfficeAssignment)
    .Include(i => i.Courses.Select(c => c.Department))
     .OrderBy(i => i.LastName);

Die zweite Include Methode lädt Courses, und für jeden geladenen Kurs wird eifrig für die Course.Department Navigationseigenschaft geladen.

.Include(i => i.Courses.Select(c => c.Department))

Wie bereits erwähnt, ist "Eager Loading" nicht erforderlich, wird jedoch durchgeführt, um die Leistung zu verbessern. Da für die Ansicht immer die Entität OfficeAssignment erforderlich ist, ist es effizienter, sie in derselben Abfrage abzurufen. Course Entitäten sind erforderlich, wenn ein Kursleiter auf der Webseite ausgewählt wird, sodass eifriges Laden besser ist als verzögertes Laden nur, wenn die Seite häufiger mit einem ausgewählten Kurs als ohne angezeigt wird.

Wenn eine Dozenten-ID ausgewählt wurde, wird der ausgewählte Dozent aus der Liste der Dozenten im Ansichtsmodell abgerufen. Die Eigenschaft Courses des Ansichtsmodells wird dann mit den Course-Entitäten aus der Navigationseigenschaft Courses dieser Lehrkraft geladen.

if (id != null)
{
    ViewBag.InstructorID = id.Value;
    viewModel.Courses = viewModel.Instructors.Where(i => i.InstructorID == id.Value).Single().Courses;
}

Die Where -Methode gibt eine Auflistung zurück, aber in diesem Fall führen die an diese Methode übergebenen Kriterien dazu, dass nur eine einzelne Instructor Entität zurückgegeben wird. Die Methode Single konvertiert die Sammlung in eine einzelne Instructor-Entität, die Ihnen Zugriff auf die Courses-Eigenschaft dieser Entität erteilt.

Sie verwenden die Single-Methode für eine Sammlung, wenn Sie wissen, dass die Sammlung nur ein Element enthält. Die Single -Methode löst eine Ausnahme aus, wenn die an sie übergebene Auflistung leer ist oder mehrere Elemente vorhanden sind. Eine Alternative ist SingleOrDefault, der einen Standardwert (null in diesem Fall) zurückgibt, wenn die Auflistung leer ist. In diesem Fall würde dies jedoch immer noch zu einer Ausnahme führen (von dem Versuch, eine Courses Eigenschaft für einen null Verweis zu finden), und die Ausnahmemeldung würde die Ursache des Problems weniger eindeutig angeben. Wenn Sie die Single -Methode aufrufen, können Sie auch die Where Bedingung übergeben, anstatt die Where -Methode separat aufzurufen:

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

anstelle von:

.Where(I => i.InstructorID == id.Value).Single()

Wenn ein Kurs ausgewählt wurde, wird der ausgewählte Kurs aus der Kursliste im Ansichtsmodell abgerufen. Anschließend wird die -Eigenschaft des Ansichtsmodells Enrollments mit den Entitäten aus der Enrollment Navigationseigenschaft dieses Kurses Enrollments geladen.

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

Ändern der Kursleiterindexansicht

Ersetzen Sie in Views\Instructor\Index.cshtml den vorhandenen Code durch den folgenden Code. Die Änderungen werden hervorgehoben:

@model ContosoUniversity.ViewModels.InstructorIndexData

@{
    ViewBag.Title = "Instructors";
}

<h2>Instructors</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table> 
    <tr> 
        <th></th> 
        <th>Last Name</th> 
        <th>First Name</th> 
        <th>Hire Date</th> 
        <th>Office</th>
    </tr> 
    @foreach (var item in Model.Instructors) 
    { 
        string selectedRow = ""; 
        if (item.InstructorID == ViewBag.InstructorID) 
        { 
            selectedRow = "selectedrow"; 
        } 
        <tr class="@selectedRow" valign="top"> 
            <td> 
                @Html.ActionLink("Select", "Index", new { id = item.InstructorID }) | 
                @Html.ActionLink("Edit", "Edit", new { id = item.InstructorID }) | 
                @Html.ActionLink("Details", "Details", new { id = item.InstructorID }) | 
                @Html.ActionLink("Delete", "Delete", new { id = item.InstructorID }) 
            </td> 
            <td> 
                @item.LastName 
            </td> 
            <td> 
                @item.FirstMidName 
            </td> 
            <td> 
                @Html.DisplayFor(modelItem => item.HireDate)
            </td> 
            <td> 
                @if (item.OfficeAssignment != null) 
                { 
                    @item.OfficeAssignment.Location  
                } 
            </td> 
        </tr> 
    } 
</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.

  • Die Zeilenlinkspalten wurden nach links verschoben.

  • Die Spalte FullName wurde entfernt.

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

    <td> 
        @if (item.OfficeAssignment != null) 
        { 
            @item.OfficeAssignment.Location  
        } 
    </td>
    
  • Code hinzugefügt, der dem tr Element des ausgewählten Dozenten dynamisch hinzugefügt class="selectedrow" wird. Dadurch wird eine Hintergrundfarbe für die ausgewählte Zeile mithilfe der CSS-Klasse festgelegt, die Sie zuvor erstellt haben. (Das valign Attribut ist im folgenden Tutorial nützlich, wenn Sie der Tabelle eine Spalte mit mehreren Zeilen hinzufügen.)

    string selectedRow = ""; 
    if (item.InstructorID == ViewBag.InstructorID) 
    { 
        selectedRow = "selectedrow"; 
    } 
    <tr class="@selectedRow" valign="top">
    
  • Direkt vor den anderen Links in jeder Zeile wurde eine neue ActionLink Bezeichnung mit der Bezeichnung Select hinzugefügt, wodurch die ausgewählte Dozenten-ID an die Index -Methode gesendet wird.

Führen Sie die Anwendung aus, und wählen Sie die Registerkarte Dozenten aus. Auf der Seite werden die Location -Eigenschaft verwandter OfficeAssignment Entitäten und eine leere Tabellenzelle angezeigt, wenn keine verknüpfte OfficeAssignment Entität vorhanden ist.

Instructors_index_page_with_nothing_selected

Fügen Sie in der Datei Views\Instructor\Index.cshtml nach dem schließenden table Element (am Ende der Datei) den folgenden hervorgehobenen Code hinzu. Dadurch wird eine Liste von Kursen angezeigt, die sich auf einen Kursleiter beziehen, wenn ein Kursleiter ausgewählt wird.

<td> 
                @if (item.OfficeAssignment != null) 
                { 
                    @item.OfficeAssignment.Location  
                } 
            </td> 
        </tr> 
    } 
</table>

@if (Model.Courses != null) 
{ 
    <h3>Courses Taught by Selected Instructor</h3> 
<table> 
    <tr> 
        <th></th> 
        <th>ID</th> 
        <th>Title</th> 
        <th>Department</th> 
    </tr> 
 
    @foreach (var item in Model.Courses) 
    { 
        string selectedRow = ""; 
        if (item.CourseID == ViewBag.CourseID) 
        { 
            selectedRow = "selectedrow"; 
        } 
    <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. Außerdem wird ein Select Link bereitgestellt, der die ID des ausgewählten Kurses an die Index Aktionsmethode sendet.

Hinweis

Die CSS-Datei wird von Browsern zwischengespeichert. Wenn die Änderungen beim Ausführen der Anwendung nicht angezeigt werden, führen Sie eine harte Aktualisierung durch (halten Sie die STRG-TASTE gedrückt, während Sie auf die Schaltfläche Aktualisieren klicken, oder drücken Sie STRG+F5).

Führen Sie die Seite aus, und 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_with_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> 
        <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.

Führen Sie die Seite aus, und wählen Sie einen Dozenten aus. Wählen Sie dann einen Kurs aus, um die Liste der registrierten Studenten und deren Noten einzusehen.

Screenshot der Seite

Hinzufügen von expliziten Ladevorgängen

Öffnen Sie InstructorController.cs , und sehen Sie sich an, wie die Index Methode die Liste der Registrierungen für einen ausgewählten Kurs abruft:

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

Wenn Sie die Liste der Kursleiter abgerufen haben, haben Sie eager loading für die Courses Navigationseigenschaft und für die Department Eigenschaft jedes Kurses angegeben. Anschließend fügen Sie die Courses Auflistung in das Ansichtsmodell ein, und jetzt greifen Sie von einer Entität in dieser Sammlung auf die Enrollments Navigationseigenschaft zu. Da Sie kein eager loading für die Course.Enrollments Navigationseigenschaft angegeben haben, werden die Daten aus dieser Eigenschaft auf der Seite angezeigt, da sie verzögert geladen werden.

Wenn Sie das verzögerte Laden deaktiviert haben, ohne den Code auf andere Weise zu ändern, wäre die Enrollments Eigenschaft null, unabhängig davon, wie viele Registrierungen der Kurs tatsächlich hatte. In diesem Fall müssen Sie zum Laden der Enrollments Eigenschaft entweder eager loading oder explizit laden angeben. Sie haben bereits erfahren, wie Sie eifrig laden. Um ein Beispiel für das explizite Laden anzuzeigen, ersetzen Sie die Index -Methode durch den folgenden Code, der die Enrollments -Eigenschaft explizit lädt. Der geänderte Code wird hervorgehoben.

public ActionResult Index(int? id, int? courseID)
{
    var viewModel = new InstructorIndexData();

    viewModel.Instructors = db.Instructors
        .Include(i => i.OfficeAssignment)
        .Include(i => i.Courses.Select(c => c.Department))
        .OrderBy(i => i.LastName);

    if (id != null)
    {
        ViewBag.InstructorID = id.Value;
        viewModel.Courses = viewModel.Instructors.Where(
            i => i.InstructorID == id.Value).Single().Courses;
    }
    
    if (courseID != null)
    {
        ViewBag.CourseID = courseID.Value;
        var selectedCourse = viewModel.Courses.Where(x => x.CourseID == courseID).Single();
        db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();
        foreach (Enrollment enrollment in selectedCourse.Enrollments)
        {
            db.Entry(enrollment).Reference(x => x.Student).Load();
        }

        viewModel.Enrollments = selectedCourse.Enrollments;
    }

    return View(viewModel);
}

Nach dem Abrufen der ausgewählten Course Entität lädt der neue Code explizit die Navigationseigenschaft dieses Kurses Enrollments :

db.Entry(selectedCourse).Collection(x => x.Enrollments).Load();

Anschließend wird explizit die zugehörige Student Entität jeder Enrollment Entität geladen:

db.Entry(enrollment).Reference(x => x.Student).Load();

Beachten Sie, dass Sie die Collection -Methode verwenden, um eine Sammlungseigenschaft zu laden, aber für eine Eigenschaft, die nur eine Entität enthält, verwenden Sie die Reference -Methode. Sie können die Seite Instructor Index jetzt ausführen, und Es wird kein Unterschied in der Anzeige auf der Seite angezeigt, obwohl Sie geändert haben, wie die Daten abgerufen werden.

Zusammenfassung

Sie haben jetzt alle drei Möglichkeiten (faul, eifrig und explizit) verwendet, um verwandte Daten in Navigationseigenschaften zu laden. Das nächste Tutorial zeigt die Aktualisierung verwandter Daten.

Links zu anderen Entity Framework-Ressourcen finden Sie im ASP.NET Data Access Content Map.