Samouczek: odczytywanie powiązanych danych za pomocą platformy EF w aplikacji MVC ASP.NET

W poprzednim samouczku ukończono model danych szkoły. W tym samouczku odczytasz i wyświetlisz powiązane dane — czyli dane ładowane przez program Entity Framework do właściwości nawigacji.

Na poniższych ilustracjach przedstawiono strony, z którymi będziesz pracować.

Zrzut ekranu przedstawiający stronę Kursy z listą kursów.

Instructors_index_page_with_instructor_and_course_selected

Pobieranie ukończonego projektu

Przykładowa aplikacja internetowa Contoso University demonstruje sposób tworzenia aplikacji MVC 5 ASP.NET przy użyciu platformy Entity Framework 6 Code First i programu Visual Studio. Aby uzyskać informacje na temat serii samouczków, zobacz pierwszy samouczek z serii.

W tym samouczku zostały wykonane następujące czynności:

  • Dowiedz się, jak ładować powiązane dane
  • Tworzenie strony Kursy
  • Strona Tworzenie instruktorów

Wymagania wstępne

Istnieje kilka sposobów ładowania powiązanych danych przez program Entity Framework do właściwości nawigacji jednostki:

  • Ładowanie z opóźnieniem. Gdy jednostka jest najpierw odczytywana, powiązane dane nie są pobierane. Jednak przy pierwszej próbie uzyskania dostępu do właściwości nawigacji automatycznie pobierane są dane wymagane dla tej właściwości nawigacji. Powoduje to wysłanie wielu zapytań do bazy danych — po jednym dla samej jednostki i po jednym przy każdym pobraniu powiązanych danych dla jednostki. Klasa DbContext domyślnie włącza ładowanie z opóźnieniem.

    Lazy_loading_example

  • Chętny do ładowania. Gdy jednostka jest odczytywana, powiązane dane są pobierane wraz z nią. Zazwyczaj powoduje to utworzenie pojedynczego zapytania sprzężenia, które pobiera wszystkie potrzebne dane. Chcesz określić chętne ładowanie przy użyciu Include metody .

    Eager_loading_example

  • Jawne ładowanie. Jest to podobne do ładowania z opóźnieniem, z tą różnicą, że jawnie pobrano powiązane dane w kodzie; Nie następuje to automatycznie, gdy uzyskujesz dostęp do właściwości nawigacji. Powiązane dane są ładowane ręcznie przez pobranie wpisu menedżera stanu obiektu dla jednostki i wywołanie metody Collection.Load dla kolekcji lub metody Reference.Load dla właściwości, które przechowują pojedynczą jednostkę. (W poniższym przykładzie, jeśli chcesz załadować właściwość nawigacji Administrator, możesz zastąpić Collection(x => x.Courses) ciąg ciągiem Reference(x => x.Administrator).) Zwykle używa się jawnego ładowania tylko wtedy, gdy ładowanie z opóźnieniem zostało wyłączone.

    Explicit_loading_example

Ponieważ nie pobierają natychmiast wartości właściwości, ładowanie leniwe i jawne ładowanie są również nazywane odroczonym ładowaniem.

Zagadnienia dotyczące wydajności

Jeśli wiesz, że potrzebujesz powiązanych danych dla każdej pobranej jednostki, chętne ładowanie często zapewnia najlepszą wydajność, ponieważ pojedyncze zapytanie wysyłane do bazy danych jest zwykle bardziej wydajne niż oddzielne zapytania dla każdej pobranej jednostki. Na przykład w powyższych przykładach załóżmy, że każdy dział ma dziesięć powiązanych kursów. Przykład chętnego ładowania spowoduje utworzenie tylko jednego zapytania (sprzężenia) i pojedynczej rundy do bazy danych. Przykładowe ładowanie z opóźnieniem i jawne ładowanie spowodowałoby jedenaście zapytań i jedenaście rund do bazy danych. Dodatkowe rundy do bazy danych są szczególnie szkodliwe dla wydajności, gdy opóźnienie jest wysokie.

Z drugiej strony w niektórych scenariuszach ładowanie leniwe jest bardziej wydajne. Duże obciążenie może spowodować wygenerowanie bardzo złożonego sprzężenia, co SQL Server nie może wydajnie przetworzyć. Jeśli jednak chcesz uzyskać dostęp do właściwości nawigacji jednostki tylko dla podzbioru przetwarzanych jednostek, ładowanie leniwe może działać lepiej, ponieważ chętne ładowanie pobiera więcej danych niż potrzebujesz. Jeśli wydajność jest krytyczna, najlepiej przetestować wydajność na oba sposoby, aby dokonać najlepszego wyboru.

Ładowanie z opóźnieniem może maskować kod, który powoduje problemy z wydajnością. Na przykład kod, który nie określa chętnego lub jawnego ładowania, ale przetwarza dużą liczbę jednostek i używa kilku właściwości nawigacji w każdej iteracji może być bardzo nieefektywny (ze względu na wiele rund do bazy danych). Aplikacja, która dobrze sprawdza się podczas programowania przy użyciu lokalnego serwera SQL, może mieć problemy z wydajnością po przeniesieniu do usługi Azure SQL Database z powodu zwiększonego opóźnienia i opóźnionego ładowania. Profilowanie zapytań bazy danych przy realistycznym obciążeniu testowym pomoże określić, czy ładowanie z opóźnieniem jest odpowiednie. Aby uzyskać więcej informacji, zobacz Demystifying Entity Framework Strategies: Loading Related Data and Using the Entity Framework to Reduce Network Latency to Usługi SQL Azure (Demystifying Entity Framework Strategies: Loading Related Data and Using the Entity Framework to Reduce Network Latency to Usługi SQL Azure ( Demystifying Entity Framework Strategies: Loading Related Data and Using the Entity Framework to Reduce Network Latency to Usługi SQL Azure (Demystifying Entity Framework Strategies:

Wyłącz ładowanie z opóźnieniem przed serializacji

Jeśli podczas serializacji pozostawisz włączone opóźnienie ładowania, możesz wykonać zapytanie znacznie większej ilości danych niż planowano. Serializacja zazwyczaj działa przez uzyskanie dostępu do każdej właściwości w wystąpieniu typu. Wyzwalacze dostępu do właściwości są ładowane z opóźnieniem, a te ładowane jednostki są serializowane. Następnie proces serializacji uzyskuje dostęp do każdej właściwości jednostek ładowanych z opóźnieniem, co może spowodować jeszcze bardziej leniwe ładowanie i serializacji. Aby zapobiec tej reakcji łańcucha ucieczki, przed serializacji jednostki należy wyłączyć opóźnienie ładowania.

Serializacja może być również skomplikowana przez klasy serwerów proxy używane przez program Entity Framework, zgodnie z opisem w samouczku Advanced Scenarios (Scenariusze zaawansowane).

Jednym ze sposobów uniknięcia problemów z serializacji jest serializowanie obiektów transferu danych (DTO) zamiast obiektów jednostek, jak pokazano w samouczku Korzystanie z internetowego interfejsu API z programem Entity Framework .

Jeśli nie używasz obiektów DTO, możesz wyłączyć ładowanie leniwe i uniknąć problemów z serwerem proxy, wyłączając tworzenie serwera proxy.

Oto kilka innych sposobów wyłączania ładowania z opóźnieniem:

  • W przypadku określonych właściwości nawigacji pomiń virtual słowo kluczowe podczas deklarowania właściwości .

  • Dla wszystkich właściwości nawigacji ustaw LazyLoadingEnabledfalsewartość , umieść następujący kod w konstruktorze klasy kontekstu:

    this.Configuration.LazyLoadingEnabled = false;
    

Tworzenie strony Kursy

Jednostka Course zawiera właściwość nawigacji zawierającą Department jednostkę działu, do której przypisano kurs. Aby wyświetlić nazwę przypisanego działu na liście kursów, musisz pobrać Name właściwość z Department jednostki, która znajduje się we Course.Department właściwości nawigacji.

Utwórz kontroler o nazwie CourseController (nie CoursesController) dla Course typu jednostki, używając tych samych opcji kontrolera MVC 5 z widokami, przy użyciu szkieletu Student programu Entity Framework, który został wcześniej utworzony dla kontrolera:

Ustawienie Wartość
Klasa modelu Wybierz pozycję Kurs (ContosoUniversity.Models).
Klasa kontekstu danych Wybierz pozycję SchoolContext (ContosoUniversity.DAL).
Nazwa kontrolera Wprowadź CourseController. Ponownie, nie CoursesController z s. Po wybraniu opcji Course (ContosoUniversity.Models) wartość nazwy kontrolera została automatycznie wypełniona. Musisz zmienić wartość.

Pozostaw pozostałe wartości domyślne i dodaj kontroler.

Otwórz plik Controllers\CourseController.cs i przyjrzyj się metodzie Index :

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

Automatyczne tworzenie szkieletów określiło chętne ładowanie właściwości Department nawigacji przy użyciu Include metody .

Otwórz plik Views\Course\Index.cshtml i zastąp kod szablonu następującym kodem. Zmiany są wyróżnione:

@model IEnumerable<ContosoUniversity.Models.Course>

@{
    ViewBag.Title = "Courses";
}

<h2>Courses</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>
            @Html.DisplayNameFor(model => model.CourseID)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Title)
        </th>
        <th>
            @Html.DisplayNameFor(model => model.Credits)
        </th>
        <th>
            Department
        </th>
        <th></th>
    </tr>

@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>
            @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>
    </tr>
}

</table>

Wprowadzono następujące zmiany w kodzie szkieletowym:

  • Zmieniono nagłówek z Indeks na Kursy.
  • Dodano kolumnę Liczba , która pokazuje CourseID wartość właściwości. Domyślnie klucze podstawowe nie są szkieletowe, ponieważ zwykle są one bez znaczenia dla użytkowników końcowych. Jednak w tym przypadku klucz podstawowy ma znaczenie i chcesz go pokazać.
  • Przeniesiono kolumnę Department (Dział ) z prawej strony i zmieniono jej nagłówek. Ruszter poprawnie wybrał Name wyświetlanie właściwości z Department jednostki, ale na stronie Kurs nagłówek kolumny powinien mieć wartość Dział , a nie Nazwa.

Zwróć uwagę, że w kolumnie Dział szkielet kodu wyświetla Name właściwość Department jednostki załadowanej do Department właściwości nawigacji:

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

Uruchom stronę (wybierz kartę Kursy na stronie głównej Contoso University), aby wyświetlić listę z nazwami działów.

Strona Tworzenie instruktorów

W tej sekcji utworzysz kontroler i widok Instructor jednostki, aby wyświetlić stronę Instruktorzy. Ta strona odczytuje i wyświetla powiązane dane w następujący sposób:

  • Lista instruktorów zawiera powiązane dane z OfficeAssignment jednostki. Jednostki Instructor i OfficeAssignment znajdują się w relacji jeden do zera lub jednego. Użyjesz chętnego OfficeAssignment ładowania dla jednostek. Jak wyjaśniono wcześniej, chętne ładowanie jest zwykle bardziej wydajne, gdy potrzebne są powiązane dane dla wszystkich pobranych wierszy tabeli podstawowej. W tym przypadku chcesz wyświetlić zadania biurowe dla wszystkich wyświetlanych instruktorów.
  • Gdy użytkownik wybierze instruktora, zostaną wyświetlone powiązane Course jednostki. Jednostki Instructor i Course znajdują się w relacji wiele do wielu. Użyjesz chętnego Course ładowania dla jednostek i ich powiązanych Department jednostek. W takim przypadku ładowanie z opóźnieniem może być bardziej wydajne, ponieważ potrzebne są kursy tylko dla wybranego instruktora. Jednak w tym przykładzie pokazano, jak używać chętnego ładowania do właściwości nawigacji we samych jednostkach we właściwościach nawigacji.
  • Gdy użytkownik wybierze kurs, wyświetlane są powiązane dane z Enrollments zestawu jednostek. Jednostki Course i Enrollment znajdują się w relacji jeden do wielu. Dodasz jawne ładowanie jednostek Enrollment i ich powiązanych Student jednostek. (Jawne ładowanie nie jest konieczne, ponieważ ładowanie z opóźnieniem jest włączone, ale pokazuje, jak jawnie ładować).

Tworzenie modelu widoku dla widoku indeksu instruktora

Na stronie Instruktorzy są wyświetlane trzy różne tabele. W związku z tym utworzysz model widoku zawierający trzy właściwości, z których każda przechowuje dane dla jednej z tabel.

W folderze ViewModels utwórz plik InstructorIndexData.cs i zastąp istniejący kod następującym kodem:

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

Tworzenie kontrolera instruktora i widoków

InstructorController Utwórz kontroler (nie InstruktorsController) z akcją odczytu/zapisu EF:

Ustawienie Wartość
Klasa modelu Wybierz pozycję Instruktor (ContosoUniversity.Models).
Klasa kontekstu danych Wybierz pozycję SchoolContext (ContosoUniversity.DAL).
Nazwa kontrolera Wprowadź ciąg InstructorController. Ponownie, nie InstruktorzyController z s. Po wybraniu opcji Course (ContosoUniversity.Models) wartość nazwy kontrolera została automatycznie wypełniona. Musisz zmienić wartość.

Pozostaw pozostałe wartości domyślne i dodaj kontroler.

Otwórz plik Controllers\InstructorController.cs i dodaj instrukcję using dla ViewModels przestrzeni nazw:

using ContosoUniversity.ViewModels;

Szkielet kodu w metodzie Index określa chętne ładowanie tylko dla OfficeAssignment właściwości nawigacji:

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

Zastąp metodę Index następującym kodem, aby załadować dodatkowe powiązane dane i umieścić ją w modelu widoku:

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.ID == 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);
}

Metoda akceptuje opcjonalne dane trasy (id) i parametr ciągu zapytania (courseID), który dostarcza wartości identyfikatorów wybranego instruktora i wybranego kursu oraz przekazuje wszystkie wymagane dane do widoku. Parametry są dostarczane przez hiperlinki Wybierz na stronie.

Kod rozpoczyna się od utworzenia wystąpienia modelu widoku i umieszczenia go na liście instruktorów. Kod określa chętne ładowanie dla Instructor.OfficeAssignment właściwości i Instructor.Courses nawigacji.

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

Druga Include metoda ładuje kursy, a dla każdego załadowanego kursu trwa ładowanie do Course.Department właściwości nawigacji.

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

Jak wspomniano wcześniej, ładowanie chętne nie jest wymagane, ale jest wykonywane w celu zwiększenia wydajności. Ponieważ widok zawsze wymaga OfficeAssignment jednostki, bardziej wydajne jest pobranie tej jednostki w tym samym zapytaniu. Course jednostki są wymagane, gdy instruktor jest wybrany na stronie internetowej, więc chętne ładowanie jest lepsze niż ładowanie leniwe tylko wtedy, gdy strona jest wyświetlana częściej z wybranym kursem niż bez.

Jeśli wybrano identyfikator instruktora, wybrany instruktor zostanie pobrany z listy instruktorów w modelu widoku. Właściwość modelu Courses widoku jest następnie ładowana z jednostkami Course z właściwości nawigacji tego instruktora Courses .

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

Metoda Where zwraca kolekcję, ale w tym przypadku kryteria przekazane do tej metody powodują zwrócenie tylko jednej Instructor jednostki. Metoda Single konwertuje kolekcję na pojedynczą Instructor jednostkę, która zapewnia dostęp do właściwości tej Courses jednostki.

Używasz metody Single w kolekcji, gdy wiadomo, że kolekcja będzie miała tylko jeden element. Metoda Single zgłasza wyjątek, jeśli kolekcja przekazana do niej jest pusta lub jeśli istnieje więcej niż jeden element. Alternatywą jest SingleOrDefault, która zwraca wartość domyślną (null w tym przypadku), jeśli kolekcja jest pusta. Jednak w tym przypadku nadal powoduje to wyjątek (od próby znalezienia Courses właściwości w odwołaniu null ), a komunikat o wyjątku będzie mniej wyraźnie wskazywać przyczynę problemu. Po wywołaniu Single metody można również przekazać Where warunek zamiast wywoływać metodę Where oddzielnie:

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

Zamiast:

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

Następnie, jeśli wybrano kurs, wybrany kurs jest pobierany z listy kursów w modelu widoku. Następnie właściwość modelu Enrollments widoku jest ładowana z jednostkami Enrollment z właściwości nawigacji tego kursu Enrollments .

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

Modyfikowanie widoku indeksu instruktora

W pliku Views\Instructor\Index.cshtml zastąp kod szablonu następującym kodem. Zmiany są wyróżnione:

@model ContosoUniversity.ViewModels.InstructorIndexData

@{
    ViewBag.Title = "Instructors";
}

<h2>Instructors</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table class="table">
    <tr>
        <th>Last Name</th>
        <th>First Name</th>
        <th>Hire Date</th>
        <th>Office</th>
        <th></th>
    </tr>

    @foreach (var item in Model.Instructors)
    {
        string selectedRow = "";
        if (item.ID == ViewBag.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>
                @Html.ActionLink("Select", "Index", new { id = item.ID }) |
                @Html.ActionLink("Edit", "Edit", new { id = item.ID }) |
                @Html.ActionLink("Details", "Details", new { id = item.ID }) |
                @Html.ActionLink("Delete", "Delete", new { id = item.ID })
            </td>
        </tr>
    }

    </table>

Wprowadzono następujące zmiany w istniejącym kodzie:

  • Zmieniono klasę modelu na InstructorIndexData.

  • Zmieniono tytuł strony z Indeks na Instruktorzy.

  • Dodano kolumnę pakietu Office , która jest wyświetlana item.OfficeAssignment.Location tylko wtedy, gdy item.OfficeAssignment nie ma wartości null. (Ponieważ jest to relacja jeden do zera lub jednego, może nie być powiązana OfficeAssignment jednostka).

    <td> 
        @if (item.OfficeAssignment != null) 
        { 
            @item.OfficeAssignment.Location  
        } 
    </td>
    
  • Dodano kod, który będzie dynamicznie dodawany class="success" do tr elementu wybranego instruktora. Spowoduje to ustawienie koloru tła dla wybranego wiersza przy użyciu klasy Bootstrap .

    string selectedRow = ""; 
    if (item.InstructorID == ViewBag.InstructorID) 
    { 
        selectedRow = "success"; 
    } 
    <tr class="@selectedRow" valign="top">
    
  • Dodano nową ActionLink etykietę Wybierz bezpośrednio przed innymi linkami w każdym wierszu, co powoduje wysłanie wybranego identyfikatora instruktora Index do metody.

Uruchom aplikację i wybierz kartę Instruktorzy . Strona wyświetla Location właściwość powiązanych OfficeAssignment jednostek i pustą komórkę tabeli, gdy nie ma powiązanej OfficeAssignment jednostki.

W pliku Views\Instructor\Index.cshtml po elememencie zamykającym table (na końcu pliku) dodaj następujący kod. Ten kod wyświetla listę kursów związanych z instruktorem po wybraniu instruktora.

@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 == ViewBag.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>
}

Ten kod odczytuje Courses właściwość modelu widoku, aby wyświetlić listę kursów. Zawiera Select również hiperlink, który wysyła identyfikator wybranego kursu do Index metody akcji.

Uruchom stronę i wybierz instruktora. Teraz zostanie wyświetlona siatka zawierająca kursy przypisane do wybranego instruktora, a dla każdego kursu zostanie wyświetlona nazwa przypisanego działu.

Po właśnie dodanym bloku kodu dodaj następujący kod. Spowoduje to wyświetlenie listy uczniów, którzy są zarejestrowani w kursie po wybraniu tego kursu.

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

Ten kod odczytuje Enrollments właściwość modelu widoku, aby wyświetlić listę uczniów zarejestrowanych w kursie.

Uruchom stronę i wybierz instruktora. Następnie wybierz kurs, aby wyświetlić listę zarejestrowanych uczniów i ich ocen.

Dodawanie ładowania jawnego

Otwórz plik InstructorController.cs i sprawdź, jak Index metoda pobiera listę rejestracji dla wybranego kursu:

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

Po pobraniu listy instruktorów określono chętne ładowanie właściwości Courses nawigacji i dla Department właściwości każdego kursu. Następnie umieścisz Courses kolekcję w modelu widoku, a teraz uzyskujesz dostęp do Enrollments właściwości nawigacji z jednej jednostki w tej kolekcji. Ponieważ nie określono chętnego Course.Enrollments ładowania dla właściwości nawigacji, dane z tej właściwości są wyświetlane na stronie w wyniku leniwego ładowania.

Jeśli wyłączono ładowanie leniwe bez zmiany kodu w inny sposób, Enrollments właściwość będzie mieć wartość null niezależnie od liczby rejestracji, które rzeczywiście miały kurs. W takim przypadku, aby załadować Enrollments właściwość, należy określić chętne ładowanie lub jawne ładowanie. Wiesz już, jak wykonywać chętne ładowanie. Aby zobaczyć przykład jawnego ładowania, zastąp Index metodę następującym kodem, który jawnie ładuje Enrollments właściwość . Zmieniono kod.

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.ID == id.Value).Single().Courses;
    }
    
    if (courseID != null)
    {
        ViewBag.CourseID = courseID.Value;
        // Lazy loading
        //viewModel.Enrollments = viewModel.Courses.Where(
        //    x => x.CourseID == courseID).Single().Enrollments;
        // Explicit loading
        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);
}

Po uzyskaniu wybranej Course jednostki nowy kod jawnie ładuje właściwość nawigacji tego kursu Enrollments :

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

Następnie jawnie ładuje jednostkę powiązaną z każdą Enrollment jednostką Student :

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

Zwróć uwagę, że metoda służy Collection do ładowania właściwości kolekcji, ale w przypadku właściwości, która zawiera tylko jedną jednostkę, należy użyć Reference metody .

Uruchom teraz stronę Indeks instruktora i nie zobaczysz różnicy w tym, co jest wyświetlane na stronie, chociaż zmieniono sposób pobierania danych.

Uzyskiwanie kodu

Pobieranie ukończonego projektu

Dodatkowe zasoby

Linki do innych zasobów platformy Entity Framework można znaleźć w ASP.NET Dostęp do danych — zalecane zasoby.

Następne kroki

W tym samouczku zostały wykonane następujące czynności:

  • Dowiedz się, jak ładować powiązane dane
  • Strona Tworzenie kursów
  • Strona Tworzenie instruktorów

Przejdź do następnego artykułu, aby dowiedzieć się, jak zaktualizować powiązane dane.