Samouczek: odczytywanie powiązanych danych — ASP.NET MVC za pomocą polecenia EF Core

W poprzednim samouczku ukończono model danych Szkoły. W tym samouczku będziesz odczytywać i wyświetlać 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ć.

Courses Index page

Instructors Index page

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

  • Dowiedz się, jak ładować powiązane dane
  • Tworzenie strony Kursy
  • Tworzenie strony Instruktorzy
  • Dowiedz się więcej o jawnym ładowaniu

Wymagania wstępne

Istnieje kilka sposobów, na które oprogramowanie mapowania relacyjnego (ORM), takie jak Entity Framework, może załadować powiązane dane do właściwości nawigacji jednostki:

  • Chętne ładowanie: gdy jednostka jest odczytywana, powiązane dane są pobierane wraz z nim. Zazwyczaj powoduje to utworzenie pojedynczego zapytania sprzężenia, które pobiera wszystkie potrzebne dane. Możesz określić chętne ładowanie w programie Entity Framework Core przy użyciu Include metod i ThenInclude .

    Eager loading example

    Niektóre dane można pobrać w osobnych zapytaniach, a program EF "naprawia" właściwości nawigacji. Oznacza to, że program EF automatycznie dodaje oddzielnie pobrane jednostki, w których należą do właściwości nawigacji poprzednio pobranych jednostek. W przypadku zapytania pobierającego powiązane dane można użyć Load metody zamiast metody zwracającej listę lub obiekt, na przykład ToList lub Single.

    Separate queries example

  • Ładowanie jawne: gdy jednostka jest najpierw odczytywana, powiązane dane nie są pobierane. Napiszesz kod, który pobiera powiązane dane, jeśli są potrzebne. Podobnie jak w przypadku chętnego ładowania z oddzielnymi zapytaniami, jawne ładowanie powoduje wysłanie wielu zapytań do bazy danych. Różnica polega na tym, że w przypadku jawnego ładowania kod określa właściwości nawigacji do załadowania. W programie Entity Framework Core 1.1 można użyć Load metody do jawnego ładowania. Przykład:

    Explicit loading example

  • Ł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 pobierane są automatycznie dane wymagane dla tej właściwości nawigacji. Zapytanie jest wysyłane do bazy danych za każdym razem, gdy próbujesz pobrać dane z właściwości nawigacji po raz pierwszy. Program Entity Framework Core 1.0 nie obsługuje ładowania leniwego.

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. Załóżmy na przykład, że każdy dział ma dziesięć powiązanych kursów. Chętne ładowanie wszystkich powiązanych danych spowodowałoby tylko jedno zapytanie (sprzężenie) i jedną rundę do bazy danych. Oddzielne zapytanie dotyczące kursów dla każdego działu spowodowałoby 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 oddzielne zapytania są bardziej wydajne. Chętne ładowanie wszystkich powiązanych danych w jednym zapytaniu może spowodować wygenerowanie bardzo złożonego sprzężenia, którego program SQL Server nie może wydajnie przetworzyć. Jeśli jednak musisz uzyskać dostęp do właściwości nawigacji jednostki tylko dla podzestawu zestawu przetwarzanych jednostek, oddzielne zapytania mogą działać lepiej, ponieważ chętne ładowanie wszystkiego z góry spowoduje pobranie większej ilości danych niż potrzebujesz. Jeśli wydajność jest krytyczna, najlepiej przetestować wydajność na oba sposoby, aby dokonać najlepszego wyboru.

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 CoursesController dla Course typu jednostki przy użyciu tych samych opcji dla kontrolera MVC z widokami przy użyciu szkieletu StudentsControllerprogramu Entity Framework, który został wcześniej utworzony dla elementu , jak pokazano na poniższej ilustracji:

Add Courses controller

Otwórz CoursesController.cs i sprawdź metodę Index . Automatyczne tworzenie szkieletów określiło chętne ładowanie właściwości Department nawigacji przy użyciu Include metody .

Zastąp metodę Index następującym kodem, który używa bardziej odpowiedniej nazwy dla IQueryable obiektu zwracającego jednostki Course (courses zamiast schoolContext):

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

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

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

Wprowadzono następujące zmiany w kodzie szkieletowym:

  • Zmieniono nagłówek z Indeks na Kursy.

  • Dodano kolumnę Number (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 jest zrozumiały i chcesz go pokazać.

  • Zmieniono kolumnę Department (Dział), aby wyświetlić nazwę działu. Kod wyświetla Name właściwość Department jednostki załadowanej do Department właściwości nawigacji:

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

Uruchom aplikację i wybierz kartę Kursy , aby wyświetlić listę z nazwami działów.

Courses Index page

Tworzenie strony Instruktorzy

W tej sekcji utworzysz kontroler i widok dla Instructor jednostki, aby wyświetlić stronę Instruktorzy:

Instructors Index page

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, ładowanie chętne jest zwykle bardziej wydajne, gdy potrzebne są powiązane dane dla wszystkich pobranych wierszy tabeli podstawowej. W takim przypadku chcesz wyświetlić przydziały 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 powiązanych z Department nimi jednostek. W takim przypadku oddzielne zapytania mogą 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 w ramach samych jednostek we właściwościach nawigacji.

  • Gdy użytkownik wybierze kurs, zostaną wyświetlone powiązane dane z Enrollments zestawu jednostek. Jednostki Course i Enrollment znajdują się w relacji jeden do wielu. Użyjesz oddzielnych zapytań dla Enrollment jednostek i powiązanych z Student nimi jednostek.

Tworzenie modelu widoku dla widoku Indeks instruktora

Na stronie Instruktorzy są wyświetlane dane z trzech różnych tabel. 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 SchoolViewModels utwórz InstructorIndexData.cs i zastąp istniejący kod następującym kodem:

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

Tworzenie kontrolera instruktora i widoków

Utwórz kontroler instruktorów z akcjami odczytu/zapisu ef, jak pokazano na poniższej ilustracji:

Add Instructors controller

Otwórz InstructorsController.cs i dodaj instrukcję using dla przestrzeni nazw ViewModels:

using ContosoUniversity.Models.SchoolViewModels;

Zastąp metodę Index następującym kodem, aby wykonać chętne ładowanie powiązanych danych i umieścić ją w modelu widoku.

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

Metoda akceptuje opcjonalne dane trasy (id) i parametr ciągu zapytania (courseID), który udostępnia wartości identyfikatorów wybranego instruktora i wybranego kursu. Parametry są udostępniane przez hiperlinki Wybierz na stronie.

Kod rozpoczyna się od utworzenia wystąpienia modelu widoku i umieszczenie go na liście instruktorów. Kod określa chętne ładowanie dla Instructor.OfficeAssignment właściwości nawigacji i Instructor.CourseAssignments . CourseAssignments We właściwości właściwość Course jest ładowana, a w jej Enrollments obrębie są ładowane właściwości i Department w ramach każdej Enrollment jednostki, Student która jest ładowana.

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

Ponieważ widok zawsze wymaga OfficeAssignment jednostki, bardziej wydajne jest pobranie tej jednostki w tym samym zapytaniu. Jednostki kursu są wymagane, gdy instruktor jest wybrany na stronie internetowej, więc pojedyncze zapytanie jest lepsze niż wiele zapytań tylko wtedy, gdy strona jest wyświetlana częściej z wybranym kursem niż bez.

Kod powtarza się CourseAssignments i Course dlatego, że potrzebne są dwie właściwości z pliku Course. Pierwszy ciąg wywołań ThenInclude pobiera wartości CourseAssignment.Course, Course.Enrollmentsi Enrollment.Student.

Więcej informacji na temat dołączania wielu poziomów powiązanych danych można znaleźć tutaj.

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

W tym momencie w kodzie innym ThenInclude elementem będą właściwości nawigacji , Studentktórych nie potrzebujesz. Jednak wywołanie Include zaczyna się od Instructor właściwości, więc trzeba przejść przez łańcuch ponownie, tym razem określając Course.Department zamiast Course.Enrollments.

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

Poniższy kod jest wykonywany po wybraniu instruktora. Wybrany instruktor jest pobierany 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 CourseAssignments .

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

Metoda Where zwraca kolekcję, ale w tym przypadku kryteria przekazane do tej metody powodują zwrócenie tylko jednej jednostki Instruktor. Metoda Single konwertuje kolekcję na pojedynczą Instructor jednostkę, która zapewnia dostęp do właściwości tej CourseAssignments jednostki. Właściwość CourseAssignments zawiera CourseAssignment jednostki, z których chcesz tylko powiązane Course jednostki.

Metoda jest używana Single w kolekcji, gdy wiadomo, że kolekcja będzie zawierać 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, który nadal powoduje wyjątek (od próby znalezienia Courses właściwości w odwołaniu o wartości null), a komunikat o wyjątku będzie mniej wyraźnie wskazywać przyczynę problemu. Po wywołaniu Single metody można również przekazać warunek Where 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 zostanie pobrany 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)
{
    ViewData["CourseID"] = courseID.Value;
    viewModel.Enrollments = viewModel.Courses.Where(
        x => x.CourseID == courseID).Single().Enrollments;
}

Śledzenie a brak śledzenia

Zapytania śledzenia nie są przydatne, gdy wyniki są używane w scenariuszu tylko do odczytu. Zazwyczaj są one szybsze do wykonania, ponieważ nie ma potrzeby konfigurowania informacji śledzenia zmian. Jeśli jednostki pobrane z bazy danych nie muszą być aktualizowane, zapytanie śledzenia prawdopodobnie będzie działać lepiej niż zapytanie śledzenia.

W niektórych przypadkach zapytanie śledzenia jest bardziej wydajne niż zapytanie bez śledzenia. Aby uzyskać więcej informacji, zobacz Śledzenie a zapytania bez śledzenia.

Modyfikowanie widoku indeksu instruktora

W Views/Instructors/Index.cshtmlpliku zastąp kod szablonu następującym kodem. Zmiany są wyróżnione.

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

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, być może nie ma powiązanej jednostki OfficeAssignment).

    @if (item.OfficeAssignment != null)
    {
        @item.OfficeAssignment.Location
    }
    
  • Dodano kolumnę Kursy zawierającą kursy nauczane przez każdego instruktora. Aby uzyskać więcej informacji, zobacz sekcję Jawne przejście wiersza w Razor artykule składni.

  • Dodano kod, który warunkowo dodaje klasę CSS Bootstrap do tr elementu wybranego instruktora. Ta klasa ustawia kolor tła dla wybranego wiersza.

  • Dodano nowe hiperłącze oznaczone etykietą Wybierz bezpośrednio przed innymi linkami w każdym wierszu, co powoduje wysłanie identyfikatora wybranego Index instruktora do metody.

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

Uruchom aplikację i wybierz kartę Instruktorzy . Na stronie jest wyświetlana właściwość Location powiązanych jednostek OfficeAssignment i pusta komórka tabeli, gdy nie ma powiązanej jednostki OfficeAssignment.

Instructors Index page nothing selected

Views/Instructors/Index.cshtml W pliku po zamknięciu elementu tabeli (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 == (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>
}

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

Odśwież 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.

Instructors Index page instructor selected

Po dodaniu właśnie dodanego 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.

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

Instructors Index page instructor and course selected

Informacje o jawnym ładowaniu

Po pobraniu listy instruktorów w programie InstructorsController.csokreślono chętne ładowanie dla CourseAssignments właściwości nawigacji.

Załóżmy, że oczekujesz, że użytkownicy rzadko chcą zobaczyć rejestracje w wybranym instruktorze i kursie. W takim przypadku możesz załadować dane rejestracji tylko wtedy, gdy są wymagane. Aby zobaczyć przykład sposobu jawnego ładowania, zastąp Index metodę następującym kodem, który usuwa chętne ładowanie i Enrollments ładuje tę właściwość jawnie. Zmiany kodu są wyróżnione.

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

Nowy kod usuwa ThenInclude wywołania metody dla danych rejestracji z kodu pobierającego jednostki instruktora. Spada również AsNoTracking. Jeśli wybrano instruktora i kurs, wyróżniony kod pobiera Enrollment jednostki dla wybranego kursu oraz Student jednostki dla każdego Enrollmentkursu .

Uruchom aplikację, przejdź teraz do strony Indeks instruktorów i nie zobaczysz różnicy w tym, co jest wyświetlane na stronie, chociaż zmieniono sposób pobierania danych.

Uzyskiwanie kodu

Pobierz lub wyświetl ukończoną aplikację.

Następne kroki

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

  • Dowiedz się, jak ładować powiązane dane
  • Utworzono stronę Kursy
  • Utworzono stronę Instruktorzy
  • Informacje o jawnym ładowaniu

Przejdź do następnego samouczka, aby dowiedzieć się, jak aktualizować powiązane dane.