在本教程中,將讀取和顯示相關數(shù)據(jù)。 相關數(shù)據(jù)為 EF Core 加載到導航屬性中的數(shù)據(jù)。
如果遇到無法解決的問題,請下載或查看已完成的應用。 下載說明。
下圖顯示了本教程中已完成的頁面:
EF Core 可采用多種方式將相關數(shù)據(jù)加載到實體的導航屬性中:
課程實體包括一個帶 Department 實體的導航屬性。 Department 實體包含要分配課程的院系。
要在課程列表中顯示已分配院系的名稱:
按照為“學生”模型搭建基架中的說明操作,并對模型類使用 Course
。
上述命令為 Course 模型創(chuàng)建基架。 在 Visual Studio 中打開項目。
打開 Pages/Courses/Index.cshtml.cs 并檢查 OnGetAsync 方法。 基架引擎為 Department 導航屬性指定了預先加載。 Include 方法指定預先加載。
運行應用并選擇“課程”鏈接。 院系列顯示 DepartmentID(該項無用)。
使用以下代碼更新 OnGetAsync 方法:
C#
public async Task OnGetAsync()
{
Course = await _context.Courses
.Include(c => c.Department)
.AsNoTracking()
.ToListAsync();
}
上述代碼添加了 AsNoTracking。 由于未跟蹤返回的實體,因此 AsNoTracking 提升了性能。 未跟蹤實體,因為未在當前上下文中更新這些實體。
使用以下突出顯示的標記更新 Pages/Courses/Index.cshtml:
HTML
@page
@model ContosoUniversity.Pages.Courses.IndexModel
@{
ViewData["Title"] = "Courses";
}
<h2>Courses</h2>
<p>
<a asp-page="Create">Create New</a>
</p>
<table class="table">
<thead>
<tr>
<th>
@Html.DisplayNameFor(model => model.Course[0].CourseID)
</th>
<th>
@Html.DisplayNameFor(model => model.Course[0].Title)
</th>
<th>
@Html.DisplayNameFor(model => model.Course[0].Credits)
</th>
<th>
@Html.DisplayNameFor(model => model.Course[0].Department)
</th>
<th></th>
</tr>
</thead>
<tbody>
@foreach (var item in Model.Course)
{
<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-page="./Edit" asp-route-id="@item.CourseID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.CourseID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.CourseID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
對基架代碼進行了以下更改:
運行應用并選擇“課程”選項卡,查看包含系名稱的列表。
OnGetAsync 方法使用 Include 方法加載相關數(shù)據(jù):
C#
public async Task OnGetAsync()
{
Course = await _context.Courses
.Include(c => c.Department)
.AsNoTracking()
.ToListAsync();
}
Select 運算符僅加載所需的相關數(shù)據(jù)。 對于單個項(如 Department.Name),它使用 SQL INNER JOIN。 對于集合,它使用另一個數(shù)據(jù)庫訪問,但集合上的 Include 運算符也是如此。
以下代碼使用 Select 方法加載相關數(shù)據(jù):
C#
public IList<CourseViewModel> CourseVM { get; set; }
public async Task OnGetAsync()
{
CourseVM = await _context.Courses
.Select(p => new CourseViewModel
{
CourseID = p.CourseID,
Title = p.Title,
Credits = p.Credits,
DepartmentName = p.Department.Name
}).ToListAsync();
}
CourseViewModel:
C#
public class CourseViewModel
{
public int CourseID { get; set; }
public string Title { get; set; }
public int Credits { get; set; }
public string DepartmentName { get; set; }
}
有關完整示例的信息,請參閱 IndexSelect.cshtml 和 IndexSelect.cshtml.cs。
在本部分中,將創(chuàng)建“講師”頁。
該頁面通過以下方式讀取和顯示相關數(shù)據(jù):
“講師”頁顯示來自三個不同表格的數(shù)據(jù)。 創(chuàng)建一個視圖模型,該模型中包含表示三個表格的三個實體。
在 SchoolViewModels 文件夾中,使用以下代碼創(chuàng)建 InstructorIndexData.cs:
C#
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; }
}
}
按照為“學生”模型搭建基架中的說明操作,并對模型類使用 Instructor
。
上述命令為 Instructor 模型創(chuàng)建基架。 運行應用并導航到“講師”頁。
將 Pages/Instructors/Index.cshtml.cs 替換為以下代碼:
C#
using ContosoUniversity.Models;
using ContosoUniversity.Models.SchoolViewModels; // Add VM
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.EntityFrameworkCore;
using System.Linq;
using System.Threading.Tasks;
namespace ContosoUniversity.Pages.Instructors
{
public class IndexModel : PageModel
{
private readonly ContosoUniversity.Data.SchoolContext _context;
public IndexModel(ContosoUniversity.Data.SchoolContext context)
{
_context = context;
}
public InstructorIndexData Instructor { get; set; }
public int InstructorID { get; set; }
public async Task OnGetAsync(int? id)
{
Instructor = new InstructorIndexData();
Instructor.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
if (id != null)
{
InstructorID = id.Value;
}
}
}
}
OnGetAsync 方法接受所選講師 ID 的可選路由數(shù)據(jù)。
檢查 Pages/Instructors/Index.cshtml.cs 文件中的查詢:
C#
Instructor.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
查詢包括兩項內(nèi)容:
使用以下標記更新 Pages/Instructors/Index.cshtml:
HTML
@page "{id:int?}"
@model ContosoUniversity.Pages.Instructors.IndexModel
@{
ViewData["Title"] = "Instructors";
}
<h2>Instructors</h2>
<p>
<a asp-page="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.Instructor.Instructors)
{
string selectedRow = "";
if (item.ID == Model.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-page="./Index" asp-route-id="@item.ID">Select</a> |
<a asp-page="./Edit" asp-route-id="@item.ID">Edit</a> |
<a asp-page="./Details" asp-route-id="@item.ID">Details</a> |
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
上述標記進行以下更改:
運行應用并選擇“講師”選項卡。該頁顯示來自相關 OfficeAssignment 實體的 Location(辦公室)。如果 OfficeAssignment` 為 NULL,則顯示空白表格單元格。
單擊“選擇”鏈接。 隨即更改行樣式。
將 Pages/Instructors/Index.cshtml.cs 中的 OnGetAsync 方法替換為以下代碼:
C#
public async Task OnGetAsync(int? id, int? courseID)
{
Instructor = new InstructorIndexData();
Instructor.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
if (id != null)
{
InstructorID = id.Value;
Instructor instructor = Instructor.Instructors.Where(
i => i.ID == id.Value).Single();
Instructor.Courses = instructor.CourseAssignments.Select(s => s.Course);
}
if (courseID != null)
{
CourseID = courseID.Value;
Instructor.Enrollments = Instructor.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
}
添加 public int CourseID { get; set; }
C#
public class IndexModel : PageModel
{
private readonly ContosoUniversity.Data.SchoolContext _context;
public IndexModel(ContosoUniversity.Data.SchoolContext context)
{
_context = context;
}
public InstructorIndexData Instructor { get; set; }
public int InstructorID { get; set; }
public int CourseID { get; set; }
public async Task OnGetAsync(int? id, int? courseID)
{
Instructor = new InstructorIndexData();
Instructor.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
if (id != null)
{
InstructorID = id.Value;
Instructor instructor = Instructor.Instructors.Where(
i => i.ID == id.Value).Single();
Instructor.Courses = instructor.CourseAssignments.Select(s => s.Course);
}
if (courseID != null)
{
CourseID = courseID.Value;
Instructor.Enrollments = Instructor.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
}
檢查更新后的查詢:
C#
Instructor.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
先前查詢添加了 Department 實體。
選擇講師時 (id != null),將執(zhí)行以下代碼。 從視圖模型中的講師列表檢索所選講師。 向視圖模型的 Courses 屬性加載來自講師 CourseAssignments 導航屬性的 Course 實體。
C#
if (id != null)
{
InstructorID = id.Value;
Instructor instructor = Instructor.Instructors.Where(
i => i.ID == id.Value).Single();
Instructor.Courses = instructor.CourseAssignments.Select(s => s.Course);
}
Where 方法返回一個集合。 在前面的 Where 方法中,僅返回單個 Instructor 實體。 Single 方法將集合轉(zhuǎn)換為單個 Instructor 實體。 Instructor 實體提供對 CourseAssignments 屬性的訪問。 CourseAssignments 提供對相關 Course 實體的訪問。
當集合僅包含一個項時,集合使用 Single 方法。 如果集合為空或包含多個項,Single 方法會引發(fā)異常。 還可使用 SingleOrDefault,該方式在集合為空時返回默認值(本例中為 null)。 在空集合上使用 SingleOrDefault:
選中課程時,視圖模型的 Enrollments 屬性將填充以下代碼:
C#
if (courseID != null)
{
CourseID = courseID.Value;
Instructor.Enrollments = Instructor.Courses.Where(
x => x.CourseID == courseID).Single().Enrollments;
}
在 Pages/Instructors/Index.cshtml Razor 頁面末尾添加以下標記:
HTML
<a asp-page="./Delete" asp-route-id="@item.ID">Delete</a>
</td>
</tr>
}
</tbody>
</table>
@if (Model.Instructor.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.Instructor.Courses)
{
string selectedRow = "";
if (item.CourseID == Model.CourseID)
{
selectedRow = "success";
}
<tr class="@selectedRow">
<td>
<a asp-page="./Index" asp-route-courseID="@item.CourseID">Select</a>
</td>
<td>
@item.CourseID
</td>
<td>
@item.Title
</td>
<td>
@item.Department.Name
</td>
</tr>
}
</table>
}
上述標記顯示選中某講師時與該講師相關的課程列表。
測試應用。 單擊講師頁面上的“選擇”鏈接。
在本部分中,更新應用以顯示所選課程的學生數(shù)據(jù)。
使用以下代碼在 Pages/Instructors/Index.cshtml.cs 中更新 OnGetAsync 方法中的查詢:
C#
Instructor.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Enrollments)
.ThenInclude(i => i.Student)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
更新 Pages/Instructors/Index.cshtml。 在文件末尾添加以下標記:
HTML
@if (Model.Instructor.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.Instructor.Enrollments)
{
<tr>
<td>
@item.Student.FullName
</td>
<td>
@Html.DisplayFor(modelItem => item.Grade)
</td>
</tr>
}
</table>
}
上述標記顯示已注冊所選課程的學生列表。
刷新頁面并選擇講師。 選擇一門課程,查看已注冊的學生及其成績列表。
Single 方法可在 Where 條件中進行傳遞,無需分別調(diào)用 Where 方法:
C#
public async Task OnGetAsync(int? id, int? courseID)
{
Instructor = new InstructorIndexData();
Instructor.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Enrollments)
.ThenInclude(i => i.Student)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
if (id != null)
{
InstructorID = id.Value;
Instructor instructor = Instructor.Instructors.Single(
i => i.ID == id.Value);
Instructor.Courses = instructor.CourseAssignments.Select(
s => s.Course);
}
if (courseID != null)
{
CourseID = courseID.Value;
Instructor.Enrollments = Instructor.Courses.Single(
x => x.CourseID == courseID).Enrollments;
}
}
使用 Where 時,前面的 Single 方法不適用。 一些開發(fā)人員更喜歡 Single 方法樣式。
當前代碼為 Enrollments 和 Students 指定預先加載:
C#
Instructor.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Enrollments)
.ThenInclude(i => i.Student)
.AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
假設用戶幾乎不希望課程中顯示注冊情況。 在此情況下,可僅在請求時加載注冊數(shù)據(jù)進行優(yōu)化。 在本部分中,會更新 OnGetAsync 以使用 Enrollments 和 Students 的顯式加載。
使用以下代碼更新 OnGetAsync:
C#
public async Task OnGetAsync(int? id, int? courseID)
{
Instructor = new InstructorIndexData();
Instructor.Instructors = await _context.Instructors
.Include(i => i.OfficeAssignment)
.Include(i => i.CourseAssignments)
.ThenInclude(i => i.Course)
.ThenInclude(i => i.Department)
//.Include(i => i.CourseAssignments)
// .ThenInclude(i => i.Course)
// .ThenInclude(i => i.Enrollments)
// .ThenInclude(i => i.Student)
// .AsNoTracking()
.OrderBy(i => i.LastName)
.ToListAsync();
if (id != null)
{
InstructorID = id.Value;
Instructor instructor = Instructor.Instructors.Where(
i => i.ID == id.Value).Single();
Instructor.Courses = instructor.CourseAssignments.Select(s => s.Course);
}
if (courseID != null)
{
CourseID = courseID.Value;
var selectedCourse = Instructor.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();
}
Instructor.Enrollments = selectedCourse.Enrollments;
}
}
上述代碼取消針對注冊和學生數(shù)據(jù)的 ThenInclude 方法調(diào)用。 如果已選中課程,則突出顯示的代碼會檢索:
請注意,上述代碼為 .AsNoTracking() 加上注釋。 對于跟蹤的實體,僅可顯式加載導航屬性。
測試應用。 對用戶而言,該應用的行為與上一版本相同。
更多建議: