作者:Tom Dykstra 和 Rick Anderson
Contoso University Web 应用演示了如何使用 EF Core 和 Visual Studio 创建 Razor 页面 Web 应用。 若要了解系列教程,请参阅第一个教程。
如果遇到无法解决的问题,请下载已完成的应用,然后对比该代码与按教程所创建的代码。
本教程将介绍如何更新相关数据。 下图显示了部分已完成页面。
课程“创建”和“编辑”页的基架搭建代码具有显示院系 ID(整数)的“院系”下拉列表。 下拉列表应显示院系名称,因此这两个页面都需要院系名称列表。 若要提供该列表,请使用“创建”和“编辑”页的基类。
使用以下代码创建 Pages/Courses/DepartmentNamePageModel.cs 文件 :
using ContosoUniversity.Data; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.AspNetCore.Mvc.Rendering; using Microsoft.EntityFrameworkCore; using System.Linq; namespace ContosoUniversity.Pages.Courses { public class DepartmentNamePageModel : PageModel { public SelectList DepartmentNameSL { get; set; } public void PopulateDepartmentsDropDownList(SchoolContext _context, object selectedDepartment = null) { var departmentsQuery = from d in _context.Departments orderby d.Name // Sort by name. select d; DepartmentNameSL = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment); } } }
上面的代码创建 SelectList 以包含系名称列表。 如果指定了 selectedDepartment
,可在 SelectList
中选择该系。
“创建”和“编辑”页模型类将派生自 DepartmentNamePageModel
。
课程分配给院系。 “创建”和“编辑”页的基类提供 SelectList
,用于选择院系。 采用 SelectList
的下拉列表设置 Course.DepartmentID
外键 (FK) 属性。 EF Core 使用 Course.DepartmentID
FK 加载 Department
导航属性。
使用以下代码更新 Pages/Courses/Create.cshtml.cs :
using ContosoUniversity.Models; using Microsoft.AspNetCore.Mvc; using System.Threading.Tasks; namespace ContosoUniversity.Pages.Courses { public class CreateModel : DepartmentNamePageModel { private readonly ContosoUniversity.Data.SchoolContext _context; public CreateModel(ContosoUniversity.Data.SchoolContext context) { _context = context; } public IActionResult OnGet() { PopulateDepartmentsDropDownList(_context); return Page(); } [BindProperty] public Course Course { get; set; } public async Task<IActionResult> OnPostAsync() { var emptyCourse = new Course(); if (await TryUpdateModelAsync<Course>( emptyCourse, "course", // Prefix for form value. s => s.CourseID, s => s.DepartmentID, s => s.Title, s => s.Credits)) { _context.Courses.Add(emptyCourse); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); } // Select DepartmentID if TryUpdateModelAsync fails. PopulateDepartmentsDropDownList(_context, emptyCourse.DepartmentID); return Page(); } } }
前面的代码:
DepartmentNamePageModel
。TryUpdateModelAsync
防止过多发布。ViewData["DepartmentID"]
。 基类中的 DepartmentNameSL
是强类型模型,将用于 Razor 页面。 建议使用强类型而非弱类型。 有关详细信息,请参阅弱类型数据(ViewData 和 ViewBag)。使用以下代码更新 Pages/Courses/Create.cshtml :
@page @model ContosoUniversity.Pages.Courses.CreateModel @{ ViewData["Title"] = "Create Course"; } <h2>Create</h2> <h4>Course</h4> <hr /> <div class="row"> <div class="col-md-4"> <form method="post"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Course.CourseID" class="control-label"></label> <input asp-for="Course.CourseID" class="form-control" /> <span asp-validation-for="Course.CourseID" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Course.Title" class="control-label"></label> <input asp-for="Course.Title" class="form-control" /> <span asp-validation-for="Course.Title" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Course.Credits" class="control-label"></label> <input asp-for="Course.Credits" class="form-control" /> <span asp-validation-for="Course.Credits" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Course.Department" class="control-label"></label> <select asp-for="Course.DepartmentID" class="form-control" asp-items="@Model.DepartmentNameSL"> <option value="">-- Select Department --</option> </select> <span asp-validation-for="Course.DepartmentID" class="text-danger" /> </div> <div class="form-group"> <input type="submit" value="Create" class="btn btn-primary" /> </div> </form> </div> </div> <div> <a asp-page="Index">Back to List</a> </div> @section Scripts { @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} }
上面的代码执行以下更改:
"ViewBag.DepartmentID"
替换为 DepartmentNameSL
(来自基类)。Razor 页面使用选择标记帮助器:
<div class="form-group"> <label asp-for="Course.Department" class="control-label"></label> <select asp-for="Course.DepartmentID" class="form-control" asp-items="@Model.DepartmentNameSL"> <option value="">-- Select Department --</option> </select> <span asp-validation-for="Course.DepartmentID" class="text-danger" /> </div>
测试“创建”页。 “创建”页显示系名称,而不是系 ID。
使用以下代码更新 Pages/Courses/Edit.cshtml.cs :
using ContosoUniversity.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System.Threading.Tasks; namespace ContosoUniversity.Pages.Courses { public class EditModel : DepartmentNamePageModel { private readonly ContosoUniversity.Data.SchoolContext _context; public EditModel(ContosoUniversity.Data.SchoolContext context) { _context = context; } [BindProperty] public Course Course { get; set; } public async Task<IActionResult> OnGetAsync(int? id) { if (id == null) { return NotFound(); } Course = await _context.Courses .Include(c => c.Department).FirstOrDefaultAsync(m => m.CourseID == id); if (Course == null) { return NotFound(); } // Select current DepartmentID. PopulateDepartmentsDropDownList(_context, Course.DepartmentID); return Page(); } public async Task<IActionResult> OnPostAsync(int? id) { if (id == null) { return NotFound(); } var courseToUpdate = await _context.Courses.FindAsync(id); if (courseToUpdate == null) { return NotFound(); } if (await TryUpdateModelAsync<Course>( courseToUpdate, "course", // Prefix for form value. c => c.Credits, c => c.DepartmentID, c => c.Title)) { await _context.SaveChangesAsync(); return RedirectToPage("./Index"); } // Select DepartmentID if TryUpdateModelAsync fails. PopulateDepartmentsDropDownList(_context, courseToUpdate.DepartmentID); return Page(); } } }
这些更改与在“创建”页模型中所做的更改相似。 在上面的代码中,PopulateDepartmentsDropDownList
在院系 ID 中传递并将在下拉列表中选择该院系。
使用以下代码更新 Pages/Courses/Edit.cshtml :
@page @model ContosoUniversity.Pages.Courses.EditModel @{ ViewData["Title"] = "Edit"; } <h2>Edit</h2> <h4>Course</h4> <hr /> <div class="row"> <div class="col-md-4"> <form method="post"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <input type="hidden" asp-for="Course.CourseID" /> <div class="form-group"> <label asp-for="Course.CourseID" class="control-label"></label> <div>@Html.DisplayFor(model => model.Course.CourseID)</div> </div> <div class="form-group"> <label asp-for="Course.Title" class="control-label"></label> <input asp-for="Course.Title" class="form-control" /> <span asp-validation-for="Course.Title" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Course.Credits" class="control-label"></label> <input asp-for="Course.Credits" class="form-control" /> <span asp-validation-for="Course.Credits" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Course.Department" class="control-label"></label> <select asp-for="Course.DepartmentID" class="form-control" asp-items="@Model.DepartmentNameSL"></select> <span asp-validation-for="Course.DepartmentID" class="text-danger"></span> </div> <div class="form-group"> <input type="submit" value="Save" class="btn btn-primary" /> </div> </form> </div> </div> <div> <a asp-page="./Index">Back to List</a> </div> @section Scripts { @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} }
上面的代码执行以下更改:
"ViewBag.DepartmentID"
替换为 DepartmentNameSL
(来自基类)。该页面包含课程编号的隐藏域 (<input type="hidden">
)。 添加具有 asp-for="Course.CourseID"
的 <label>
标记帮助器也同样需要隐藏域。 用户单击“保存”时,需要 <input type="hidden">
,以便在已发布的数据中包括课程编号。
AsNoTracking 可以在不需要跟踪时提高性能。
使用以下代码更新 Pages/Courses/Delete.cshtml.cs 以添加 AsNoTracking
:
using ContosoUniversity.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; using System.Threading.Tasks; namespace ContosoUniversity.Pages.Courses { public class DeleteModel : PageModel { private readonly ContosoUniversity.Data.SchoolContext _context; public DeleteModel(ContosoUniversity.Data.SchoolContext context) { _context = context; } [BindProperty] public Course Course { get; set; } public async Task<IActionResult> OnGetAsync(int? id) { if (id == null) { return NotFound(); } Course = await _context.Courses .AsNoTracking() .Include(c => c.Department) .FirstOrDefaultAsync(m => m.CourseID == id); if (Course == null) { return NotFound(); } return Page(); } public async Task<IActionResult> OnPostAsync(int? id) { if (id == null) { return NotFound(); } Course = await _context.Courses.FindAsync(id); if (Course != null) { _context.Courses.Remove(Course); await _context.SaveChangesAsync(); } return RedirectToPage("./Index"); } } }
在 Pages/Courses/Details.cshtml.cs 文件中进行相同的更改 :
using ContosoUniversity.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; using System.Threading.Tasks; namespace ContosoUniversity.Pages.Courses { public class DetailsModel : PageModel { private readonly ContosoUniversity.Data.SchoolContext _context; public DetailsModel(ContosoUniversity.Data.SchoolContext context) { _context = context; } public Course Course { get; set; } public async Task<IActionResult> OnGetAsync(int? id) { if (id == null) { return NotFound(); } Course = await _context.Courses .AsNoTracking() .Include(c => c.Department) .FirstOrDefaultAsync(m => m.CourseID == id); if (Course == null) { return NotFound(); } return Page(); } } }
使用以下代码更新 Pages/Courses/Delete.cshtml :
@page @model ContosoUniversity.Pages.Courses.DeleteModel @{ ViewData["Title"] = "Delete"; } <h2>Delete</h2> <h3>Are you sure you want to delete this?</h3> <div> <h4>Course</h4> <hr /> <dl class="row"> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.Course.CourseID) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.Course.CourseID) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.Course.Title) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.Course.Title) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.Course.Credits) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.Course.Credits) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.Course.Department) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.Course.Department.Name) </dd> </dl> <form method="post"> <input type="hidden" asp-for="Course.CourseID" /> <input type="submit" value="Delete" class="btn btn-danger" /> | <a asp-page="./Index">Back to List</a> </form> </div>
对“详细信息”页执行相同更改。
@page @model ContosoUniversity.Pages.Courses.DetailsModel @{ ViewData["Title"] = "Details"; } <h2>Details</h2> <div> <h4>Course</h4> <hr /> <dl class="row"> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.Course.CourseID) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.Course.CourseID) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.Course.Title) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.Course.Title) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.Course.Credits) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.Course.Credits) </dd> <dt class="col-sm-2"> @Html.DisplayNameFor(model => model.Course.Department) </dt> <dd class="col-sm-10"> @Html.DisplayFor(model => model.Course.Department.Name) </dd> </dl> </div> <div> <a asp-page="./Edit" asp-route-id="@Model.Course.CourseID">Edit</a> | <a asp-page="./Index">Back to List</a> </div>
测试“创建”、“编辑”、“详细信息”和“删除”页面。
讲师可能教授任意数量的课程。 下图显示包含一系列课程复选框的讲师“编辑”页。
通过复选框可对分配给讲师的课程进行更改。 数据库中的每一门课程均有对应显示的复选框。 已分配给讲师的课程将会被选中。 用户可以通过选择或清除复选框来更改课程分配。 如果课程数过多,另一个 UI 的使用效果可能更好。 但此处所示的用于管理多对多关系的方法不会发生变化。 若要创建或删除关系,则需要使用联接实体。
使用以下代码创建 SchoolViewModels/AssignedCourseData.cs :
namespace ContosoUniversity.Models.SchoolViewModels { public class AssignedCourseData { public int CourseID { get; set; } public string Title { get; set; } public bool Assigned { get; set; } } }
AssignedCourseData
类包含的数据可用于为已分配给讲师的课程创建复选框。
创建 Pages/Instructors/InstructorCoursesPageModel.cs 基类 :
using ContosoUniversity.Data; using ContosoUniversity.Models; using ContosoUniversity.Models.SchoolViewModels; using Microsoft.AspNetCore.Mvc.RazorPages; using System.Collections.Generic; using System.Linq; namespace ContosoUniversity.Pages.Instructors { public class InstructorCoursesPageModel : PageModel { public List<AssignedCourseData> AssignedCourseDataList; public void PopulateAssignedCourseData(SchoolContext context, Instructor instructor) { var allCourses = context.Courses; var instructorCourses = new HashSet<int>( instructor.CourseAssignments.Select(c => c.CourseID)); AssignedCourseDataList = new List<AssignedCourseData>(); foreach (var course in allCourses) { AssignedCourseDataList.Add(new AssignedCourseData { CourseID = course.CourseID, Title = course.Title, Assigned = instructorCourses.Contains(course.CourseID) }); } } public void UpdateInstructorCourses(SchoolContext context, string[] selectedCourses, Instructor instructorToUpdate) { if (selectedCourses == null) { instructorToUpdate.CourseAssignments = new List<CourseAssignment>(); return; } var selectedCoursesHS = new HashSet<string>(selectedCourses); var instructorCourses = new HashSet<int> (instructorToUpdate.CourseAssignments.Select(c => c.Course.CourseID)); foreach (var course in context.Courses) { if (selectedCoursesHS.Contains(course.CourseID.ToString())) { if (!instructorCourses.Contains(course.CourseID)) { instructorToUpdate.CourseAssignments.Add( new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID }); } } else { if (instructorCourses.Contains(course.CourseID)) { CourseAssignment courseToRemove = instructorToUpdate .CourseAssignments .SingleOrDefault(i => i.CourseID == course.CourseID); context.Remove(courseToRemove); } } } } } }
InstructorCoursesPageModel
是将用于“编辑”和“创建”页模型的基类。 PopulateAssignedCourseData
读取所有 Course
实体以填充 AssignedCourseDataList
。 该代码将设置每门课程的 CourseID
和标题,并决定是否为讲师分配该课程。 HashSet 用于高效查找。
Razor 页面没有 Course 实体的集合,因此模型绑定器无法自动更新 CourseAssignments
导航属性。 可在新的 UpdateInstructorCourses
方法中更新 CourseAssignments
导航属性,而不必使用模型绑定器。 为此,需要从模型绑定中排除 CourseAssignments
属性。 此操作无需对调用 TryUpdateModel
的代码进行任何更改,因为使用的是允许列表重载,并且 CourseAssignments
不包括在该列表中。
如果未选中任何复选框,则 UpdateInstructorCourses
中的代码将使用空集合初始化 CourseAssignments
导航属性,并返回以下内容:
if (selectedCourses == null) { instructorToUpdate.CourseAssignments = new List<CourseAssignment>(); return; }
之后,代码会循环访问数据库中的所有课程,并逐一检查当前分配给讲师的课程和页面中处于选中状态的课程。 为便于高效查找,后两个集合存储在 HashSet
对象中。
如果某课程的复选框处于选中状态,但该课程不在 Instructor.CourseAssignments
导航属性中,则会将该课程添加到导航属性中的集合中。
if (selectedCoursesHS.Contains(course.CourseID.ToString())) { if (!instructorCourses.Contains(course.CourseID)) { instructorToUpdate.CourseAssignments.Add( new CourseAssignment { InstructorID = instructorToUpdate.ID, CourseID = course.CourseID }); } }
如果某课程的复选框未处于选中状态,但该课程存在 Instructor.CourseAssignments
导航属性中,则会从导航属性中删除该课程。
else { if (instructorCourses.Contains(course.CourseID)) { CourseAssignment courseToRemove = instructorToUpdate .CourseAssignments .SingleOrDefault(i => i.CourseID == course.CourseID); context.Remove(courseToRemove); } }
“编辑”页必须处理的另一个关系是 Instructor 实体与 OfficeAssignment
实体之间的一对零或一对一关系。 讲师编辑代码必须处理以下场景:
OfficeAssignment
实体。OfficeAssignment
实体。OfficeAssignment
实体。使用以下代码更新 Pages/Instructors/Edit.cshtml.cs :
using ContosoUniversity.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.EntityFrameworkCore; using System; using System.Threading.Tasks; namespace ContosoUniversity.Pages.Instructors { public class EditModel : InstructorCoursesPageModel { private readonly ContosoUniversity.Data.SchoolContext _context; public EditModel(ContosoUniversity.Data.SchoolContext context) { _context = context; } [BindProperty] public Instructor Instructor { get; set; } public async Task<IActionResult> OnGetAsync(int? id) { if (id == null) { return NotFound(); } Instructor = await _context.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.CourseAssignments).ThenInclude(i => i.Course) .AsNoTracking() .FirstOrDefaultAsync(m => m.ID == id); if (Instructor == null) { return NotFound(); } PopulateAssignedCourseData(_context, Instructor); return Page(); } public async Task<IActionResult> OnPostAsync(int? id, string[] selectedCourses) { if (id == null) { return NotFound(); } var instructorToUpdate = await _context.Instructors .Include(i => i.OfficeAssignment) .Include(i => i.CourseAssignments) .ThenInclude(i => i.Course) .FirstOrDefaultAsync(s => s.ID == id); if (instructorToUpdate == null) { return NotFound(); } if (await TryUpdateModelAsync<Instructor>( instructorToUpdate, "Instructor", i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment)) { if (String.IsNullOrWhiteSpace( instructorToUpdate.OfficeAssignment?.Location)) { instructorToUpdate.OfficeAssignment = null; } UpdateInstructorCourses(_context, selectedCourses, instructorToUpdate); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); } UpdateInstructorCourses(_context, selectedCourses, instructorToUpdate); PopulateAssignedCourseData(_context, instructorToUpdate); return Page(); } } }
前面的代码:
OfficeAssignment
、CourseAssignment
和 CourseAssignment.Course
导航属性的预先加载从数据库获取当前的 Instructor
实体。Instructor
实体。 TryUpdateModel
可防止过多发布。Instructor.OfficeAssignment
设置为 null。 当 Instructor.OfficeAssignment
为 null 时,OfficeAssignment
表中的相关行将会删除。OnGetAsync
中的 PopulateAssignedCourseData
,使用 AssignedCourseData
视图模型类为复选框提供信息。OnPostAsync
中的 UpdateInstructorCourses
,将复选框中的信息应用于将要编辑的 Instructor 实体。TryUpdateModel
失败,则调用 OnPostAsync
中的 PopulateAssignedCourseData
和 UpdateInstructorCourses
。 这些方法调用将在页面重新显示错误消息时还原页面上所输入的已分配课程数据。使用以下代码更新 Pages/Instructors/Edit.cshtml :
@page @model ContosoUniversity.Pages.Instructors.EditModel @{ ViewData["Title"] = "Edit"; } <h2>Edit</h2> <h4>Instructor</h4> <hr /> <div class="row"> <div class="col-md-4"> <form method="post"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <input type="hidden" asp-for="Instructor.ID" /> <div class="form-group"> <label asp-for="Instructor.LastName" class="control-label"></label> <input asp-for="Instructor.LastName" class="form-control" /> <span asp-validation-for="Instructor.LastName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Instructor.FirstMidName" class="control-label"></label> <input asp-for="Instructor.FirstMidName" class="form-control" /> <span asp-validation-for="Instructor.FirstMidName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Instructor.HireDate" class="control-label"></label> <input asp-for="Instructor.HireDate" class="form-control" /> <span asp-validation-for="Instructor.HireDate" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Instructor.OfficeAssignment.Location" class="control-label"></label> <input asp-for="Instructor.OfficeAssignment.Location" class="form-control" /> <span asp-validation-for="Instructor.OfficeAssignment.Location" class="text-danger" /> </div> <div class="form-group"> <div class="table"> <table> <tr> @{ int cnt = 0; foreach (var course in Model.AssignedCourseDataList) { if (cnt++ % 3 == 0) { @:</tr><tr> } @:<td> <input type="checkbox" name="selectedCourses" value="@course.CourseID" @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) /> @course.CourseID @: @course.Title @:</td> } @:</tr> } </table> </div> </div> <div class="form-group"> <input type="submit" value="Save" class="btn btn-primary" /> </div> </form> </div> </div> <div> <a asp-page="./Index">Back to List</a> </div> @section Scripts { @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} }
上面的代码将创建一个具有三列的 HTML 表。 每列均具有一个复选框和包含课程编号及标题的标题。 所有复选框均具有相同的名称(“selectedCourses”)。 使用相同名称可指示模型绑定器将它们视为一个组。 每个复选框的值特性都设置为 CourseID
。 发布页面时,模型绑定器会传递一个数组,该数组只包括所选复选框的 CourseID
值。
初次呈现复选框时,分配给讲师的课程均已选中。
注意:此处所使用的编辑讲师课程数据的方法适用于数量有限的课程。 对于规模远大于此的集合,则使用不同的 UI 和不同的更新方法会更实用和更高效。
运行应用并测试更新的讲师“编辑”页。 更改某些课程分配。 这些更改将反映在“索引”页上。
使用类似于“编辑”页的代码更新讲师“创建”页模型和 Razor 页面:
using ContosoUniversity.Models; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Threading.Tasks; namespace ContosoUniversity.Pages.Instructors { public class CreateModel : InstructorCoursesPageModel { private readonly ContosoUniversity.Data.SchoolContext _context; public CreateModel(ContosoUniversity.Data.SchoolContext context) { _context = context; } public IActionResult OnGet() { var instructor = new Instructor(); instructor.CourseAssignments = new List<CourseAssignment>(); // Provides an empty collection for the foreach loop // foreach (var course in Model.AssignedCourseDataList) // in the Create Razor page. PopulateAssignedCourseData(_context, instructor); return Page(); } [BindProperty] public Instructor Instructor { get; set; } public async Task<IActionResult> OnPostAsync(string[] selectedCourses) { var newInstructor = new Instructor(); if (selectedCourses != null) { newInstructor.CourseAssignments = new List<CourseAssignment>(); foreach (var course in selectedCourses) { var courseToAdd = new CourseAssignment { CourseID = int.Parse(course) }; newInstructor.CourseAssignments.Add(courseToAdd); } } if (await TryUpdateModelAsync<Instructor>( newInstructor, "Instructor", i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment)) { _context.Instructors.Add(newInstructor); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); } PopulateAssignedCourseData(_context, newInstructor); return Page(); } } }
@page @model ContosoUniversity.Pages.Instructors.CreateModel @{ ViewData["Title"] = "Create"; } <h2>Create</h2> <h4>Instructor</h4> <hr /> <div class="row"> <div class="col-md-4"> <form method="post"> <div asp-validation-summary="ModelOnly" class="text-danger"></div> <div class="form-group"> <label asp-for="Instructor.LastName" class="control-label"></label> <input asp-for="Instructor.LastName" class="form-control" /> <span asp-validation-for="Instructor.LastName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Instructor.FirstMidName" class="control-label"></label> <input asp-for="Instructor.FirstMidName" class="form-control" /> <span asp-validation-for="Instructor.FirstMidName" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Instructor.HireDate" class="control-label"></label> <input asp-for="Instructor.HireDate" class="form-control" /> <span asp-validation-for="Instructor.HireDate" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Instructor.OfficeAssignment.Location" class="control-label"></label> <input asp-for="Instructor.OfficeAssignment.Location" class="form-control" /> <span asp-validation-for="Instructor.OfficeAssignment.Location" class="text-danger" /> </div> <div class="form-group"> <div class="table"> <table> <tr> @{ int cnt = 0; foreach (var course in Model.AssignedCourseDataList) { if (cnt++ % 3 == 0) { @:</tr><tr> } @:<td> <input type="checkbox" name="selectedCourses" value="@course.CourseID" @(Html.Raw(course.Assigned ? "checked=\"checked\"" : "")) /> @course.CourseID @: @course.Title @:</td> } @:</tr> } </table> </div> </div> <div class="form-group"> <input type="submit" value="Create" class="btn btn-primary" /> </div> </form> </div> </div> <div> <a asp-page="Index">Back to List</a> </div> @section Scripts { @{await Html.RenderPartialAsync("_ValidationScriptsPartial");} }
测试讲师“创建”页。
使用以下代码更新 Pages/Instructors/Delete.cshtml.cs :
using ContosoUniversity.Models; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.EntityFrameworkCore; using System.Linq; using System.Threading.Tasks; namespace ContosoUniversity.Pages.Instructors { public class DeleteModel : PageModel { private readonly ContosoUniversity.Data.SchoolContext _context; public DeleteModel(ContosoUniversity.Data.SchoolContext context) { _context = context; } [BindProperty] public Instructor Instructor { get; set; } public async Task<IActionResult> OnGetAsync(int? id) { if (id == null) { return NotFound(); } Instructor = await _context.Instructors.FirstOrDefaultAsync(m => m.ID == id); if (Instructor == null) { return NotFound(); } return Page(); } public async Task<IActionResult> OnPostAsync(int? id) { if (id == null) { return NotFound(); } Instructor instructor = await _context.Instructors .Include(i => i.CourseAssignments) .SingleAsync(i => i.ID == id); if (instructor == null) { return RedirectToPage("./Index"); } var departments = await _context.Departments .Where(d => d.InstructorID == id) .ToListAsync(); departments.ForEach(d => d.InstructorID = null); _context.Instructors.Remove(instructor); await _context.SaveChangesAsync(); return RedirectToPage("./Index"); } } }
上面的代码执行以下更改:
对 CourseAssignments
导航属性使用预先加载。 必须包含 CourseAssignments
,否则删除讲师时将不会删除课程。 为避免阅读它们,可以在数据库中配置级联删除。
如果要删除的讲师被指派为任何系的管理员,则需从这些系中删除该讲师分配。
运行应用并测试“删除”页。