Bagikan melalui


Bagian 7, Razor Halaman dengan EF Core inti ASP.NET - Perbarui Data Terkait

Oleh Tom Dykstra, Jon P Smith, dan Rick Anderson

Aplikasi web Contoso University menunjukkan cara membuat Razor aplikasi web Pages menggunakan EF Core dan Visual Studio. Untuk informasi tentang seri tutorial, lihat tutorial pertama.

Jika Anda mengalami masalah yang tidak dapat Anda selesaikan, unduh aplikasi yang telah selesai dan bandingkan kode tersebut dengan apa yang Anda buat dengan mengikuti tutorial.

Tutorial ini menunjukkan cara memperbarui data terkait. Ilustrasi berikut ini memperlihatkan beberapa halaman yang telah selesai.

Halaman Edit KursusHalaman Edit Instruktur

Memperbarui halaman Buat dan Edit Kursus

Kode perancah untuk halaman Buat dan Edit Kursus memiliki daftar drop-down Departemen yang menunjukkan DepartmentID, .int Menu drop-down harus menampilkan nama Departemen, sehingga kedua halaman ini memerlukan daftar nama departemen. Untuk menyediakan daftar tersebut, gunakan kelas dasar untuk halaman Buat dan Edit.

Membuat kelas dasar untuk Membuat dan Mengedit Kursus

Buat Pages/Courses/DepartmentNamePageModel.cs file dengan kode berikut:

using ContosoUniversity.Data;
using ContosoUniversity.Models;
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(),
                nameof(Department.DepartmentID),
                nameof(Department.Name),
                selectedDepartment);
        }
    }
}

Kode sebelumnya membuat SelectList untuk berisi daftar nama departemen. Jika selectedDepartment ditentukan, departemen tersebut dipilih di SelectList.

Kelas Buat dan Edit model halaman akan berasal dari DepartmentNamePageModel.

Memperbarui model halaman Buat Kursus

Kursus ditugaskan ke Departemen. Kelas dasar untuk halaman Buat dan Edit menyediakan SelectList untuk memilih departemen. Daftar drop-down yang menggunakan SelectList properti set Course.DepartmentID kunci asing (FK). EF CoreCourse.DepartmentID menggunakan FK untuk memuat Department properti navigasi.

Buat kursus

Perbarui Pages/Courses/Create.cshtml.cs dengan kode berikut:

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

Jika Anda ingin melihat komentar kode yang diterjemahkan ke bahasa selain bahasa Inggris, beri tahu kami dalam masalah diskusi GitHub ini.

Kode sebelumnya:

Memperbarui halaman Buat Razor Kursus

Perbarui Pages/Courses/Create.cshtml dengan kode berikut:

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

Kode sebelumnya membuat perubahan berikut:

  • Mengubah keterangan dari DepartmentID ke Departemen.
  • "ViewBag.DepartmentID" Mengganti dengan DepartmentNameSL (dari kelas dasar).
  • Menambahkan opsi "Pilih Departemen". Perubahan ini merender "Pilih Departemen" di menu drop-down ketika belum ada departemen yang dipilih, bukan departemen pertama.
  • Menambahkan pesan validasi saat departemen tidak dipilih.

Halaman Razor menggunakan Pembantu Pilih Tag:

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

Uji halaman Buat. Halaman Buat menampilkan nama departemen daripada ID departemen.

Memperbarui model halaman Edit Kursus

Perbarui Pages/Courses/Edit.cshtml.cs dengan kode berikut:

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

Perubahannya mirip dengan yang dibuat dalam model halaman Buat. Dalam kode sebelumnya, PopulateDepartmentsDropDownList meneruskan ID departemen, yang memilih departemen tersebut di daftar drop-down.

Memperbarui halaman Edit Razor Kursus

Perbarui Pages/Courses/Edit.cshtml dengan kode berikut:

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

Kode sebelumnya membuat perubahan berikut:

  • Menampilkan ID kursus. Umumnya Kunci Primer (PK) entitas tidak ditampilkan. PK biasanya tidak berarti bagi pengguna. Dalam hal ini, PK adalah nomor kursus.
  • Mengubah keterangan untuk drop-down Departemen dari DepartmentID ke Departemen.
  • "ViewBag.DepartmentID" Mengganti dengan DepartmentNameSL, yang berada di kelas dasar.

Halaman berisi bidang tersembunyi (<input type="hidden">) untuk nomor kursus. Menambahkan pembantu <label> tag dengan asp-for="Course.CourseID" tidak menghilangkan kebutuhan akan bidang tersembunyi. <input type="hidden"> diperlukan agar nomor kursus disertakan dalam data yang diposting saat pengguna memilih Simpan.

Memperbarui model halaman Kursus

AsNoTracking dapat meningkatkan performa saat pelacakan tidak diperlukan.

Perbarui Pages/Courses/Delete.cshtml.cs dan Pages/Courses/Details.cshtml.cs dengan menambahkan AsNoTracking ke OnGetAsync metode:

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

Memperbarui halaman Kursus Razor

Perbarui Pages/Courses/Delete.cshtml dengan kode berikut:

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

Buat perubahan yang sama pada halaman Detail.

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

Menguji halaman Kursus

Uji halaman buat, edit, detail, dan hapus.

Memperbarui halaman Buat dan Edit instruktur

Instruktur dapat mengajarkan sejumlah kursus. Gambar berikut menunjukkan halaman Edit instruktur dengan array kotak centang kursus.

Halaman Edit Instruktur dengan kursus

Kotak centang mengaktifkan perubahan pada kursus tempat instruktur ditetapkan. Kotak centang ditampilkan untuk setiap kursus dalam database. Kursus yang ditetapkan instruktur dipilih. Pengguna dapat memilih atau menghapus kotak centang untuk mengubah penetapan kursus. Jika jumlah kursus jauh lebih besar, UI yang berbeda mungkin bekerja lebih baik. Tetapi metode mengelola hubungan banyak ke banyak yang ditunjukkan di sini tidak akan berubah. Untuk membuat atau menghapus hubungan, Anda memanipulasi entitas gabungan.

Membuat kelas untuk data kursus yang ditetapkan

Buat Models/SchoolViewModels/AssignedCourseData.cs dengan kode berikut:

namespace ContosoUniversity.Models.SchoolViewModels
{
    public class AssignedCourseData
    {
        public int CourseID { get; set; }
        public string Title { get; set; }
        public bool Assigned { get; set; }
    }
}

Kelas AssignedCourseData berisi data untuk membuat kotak centang untuk kursus yang ditetapkan ke instruktur.

Membuat kelas dasar model halaman Instruktur

Pages/Instructors/InstructorCoursesPageModel.cs Buat kelas dasar:

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

InstructorCoursesPageModel adalah kelas dasar untuk model halaman Edit dan Buat. PopulateAssignedCourseData membaca semua Course entitas untuk mengisi AssignedCourseDataList. Untuk setiap kursus, kode menetapkan CourseID, judul, dan apakah instruktur ditetapkan ke kursus atau tidak. HashSet digunakan untuk pencarian yang efisien.

Menangani lokasi kantor

Hubungan lain yang harus ditangani halaman edit adalah hubungan satu-ke-nol-atau-satu yang dimiliki entitas Instruktur dengan OfficeAssignment entitas. Kode edit instruktur harus menangani skenario berikut:

  • Jika pengguna menghapus penetapan kantor, hapus OfficeAssignment entitas.
  • Jika pengguna memasukkan penetapan kantor dan kosong, buat entitas baru OfficeAssignment .
  • Jika pengguna mengubah penetapan office, perbarui OfficeAssignment entitas.

Memperbarui model halaman Edit Instruktur

Perbarui Pages/Instructors/Edit.cshtml.cs dengan kode berikut:

using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
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.Courses)
                .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.Courses)
                .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(selectedCourses, instructorToUpdate);
                await _context.SaveChangesAsync();
                return RedirectToPage("./Index");
            }
            UpdateInstructorCourses(selectedCourses, instructorToUpdate);
            PopulateAssignedCourseData(_context, instructorToUpdate);
            return Page();
        }

        public void UpdateInstructorCourses(string[] selectedCourses,
                                            Instructor instructorToUpdate)
        {
            if (selectedCourses == null)
            {
                instructorToUpdate.Courses = new List<Course>();
                return;
            }

            var selectedCoursesHS = new HashSet<string>(selectedCourses);
            var instructorCourses = new HashSet<int>
                (instructorToUpdate.Courses.Select(c => c.CourseID));
            foreach (var course in _context.Courses)
            {
                if (selectedCoursesHS.Contains(course.CourseID.ToString()))
                {
                    if (!instructorCourses.Contains(course.CourseID))
                    {
                        instructorToUpdate.Courses.Add(course);
                    }
                }
                else
                {
                    if (instructorCourses.Contains(course.CourseID))
                    {
                        var courseToRemove = instructorToUpdate.Courses.Single(
                                                        c => c.CourseID == course.CourseID);
                        instructorToUpdate.Courses.Remove(courseToRemove);
                    }
                }
            }
        }
    }
}

Kode sebelumnya:

  • Mendapatkan entitas saat ini Instructor dari database menggunakan pemuatan bersemangat OfficeAssignment untuk properti navigasi dan Courses .
  • Memperbarui entitas yang diambil Instructor dengan nilai dari pengikat model. TryUpdateModelAsyncmencegah overposting.
  • Jika lokasi kantor kosong, atur Instructor.OfficeAssignment ke null. Ketika Instructor.OfficeAssignment null, baris terkait dalam OfficeAssignment tabel dihapus.
  • PopulateAssignedCourseData Panggilan masuk OnGetAsync untuk memberikan informasi untuk kotak centang menggunakan AssignedCourseData kelas model tampilan.
  • UpdateInstructorCourses Panggilan masuk untuk menerapkan informasi dari kotak centang ke entitas Instruktur yang sedang dieditOnPostAsync.
  • PopulateAssignedCourseData Panggilan dan UpdateInstructorCourses masuk OnPostAsync jika TryUpdateModelAsync gagal. Metode ini memanggil pemulihan data kursus yang ditetapkan yang dimasukkan di halaman ketika diputar ulang dengan pesan kesalahan.

Razor Karena halaman tidak memiliki kumpulan entitas Kursus, pengikat model tidak dapat memperbarui Courses properti navigasi secara otomatis. Alih-alih menggunakan pengikat model untuk memperbarui Courses properti navigasi, itu dilakukan dalam metode baru UpdateInstructorCourses . Oleh karena itu Anda perlu mengecualikan Courses properti dari pengikatan model. Ini tidak memerlukan perubahan apa pun pada kode yang memanggil TryUpdateModelAsync karena Anda menggunakan kelebihan beban dengan properti yang dideklarasikan dan Courses tidak ada dalam daftar sertakan.

Jika tidak ada kotak centang yang dipilih, kode dalam UpdateInstructorCourses menginisialisasi instructorToUpdate.Courses dengan koleksi kosong dan mengembalikan:

if (selectedCourses == null)
{
    instructorToUpdate.Courses = new List<Course>();
    return;
}

Kode kemudian mengulangi semua kursus dalam database dan memeriksa setiap kursus terhadap yang saat ini ditetapkan ke instruktur versus yang dipilih di halaman. Untuk memfasilitasi pencarian yang efisien, dua koleksi terakhir disimpan dalam HashSet objek.

Jika kotak centang untuk kursus dipilih tetapi kursus tidak ada di Instructor.Courses properti navigasi, kursus ditambahkan ke koleksi di properti navigasi.

if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
    if (!instructorCourses.Contains(course.CourseID))
    {
        instructorToUpdate.Courses.Add(course);
    }
}

Jika kotak centang untuk kursus tidak dipilih, tetapi kursus berada di Instructor.Courses properti navigasi, kursus akan dihapus dari properti navigasi.

else
{
    if (instructorCourses.Contains(course.CourseID))
    {
        var courseToRemove = instructorToUpdate.Courses.Single(
                                        c => c.CourseID == course.CourseID);
        instructorToUpdate.Courses.Remove(courseToRemove);
    }
}

Memperbarui halaman Edit Razor Instruktur

Perbarui Pages/Instructors/Edit.cshtml dengan kode berikut:

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

Kode sebelumnya membuat tabel HTML yang memiliki tiga kolom. Setiap kolom memiliki kotak centang dan keterangan yang berisi nomor kursus dan judul. Semua kotak centang memiliki nama yang sama ("selectedCourses"). Menggunakan nama yang sama menginformasikan pengikat model untuk memperlakukannya sebagai grup. Atribut nilai dari setiap kotak centang diatur ke CourseID. Saat halaman diposting, pengikat model meneruskan array yang terdiri dari CourseID nilai hanya untuk kotak centang yang dipilih.

Ketika kotak centang awalnya dirender, kursus yang ditetapkan ke instruktur dipilih.

Catatan: Pendekatan yang diambil di sini untuk mengedit data kursus instruktur berfungsi dengan baik ketika ada sejumlah kursus yang terbatas. Untuk koleksi yang jauh lebih besar, UI yang berbeda dan metode pembaruan yang berbeda akan lebih dapat digunakan dan efisien.

Jalankan aplikasi dan uji halaman Edit Instruktur yang diperbarui. Ubah beberapa tugas kursus. Perubahan tercermin pada halaman Indeks.

Memperbarui halaman Buat Instruktur

Perbarui model halaman Buat Instruktur dan dengan kode yang mirip dengan halaman Edit:

using ContosoUniversity.Data;
using ContosoUniversity.Models;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace ContosoUniversity.Pages.Instructors
{
    public class CreateModel : InstructorCoursesPageModel
    {
        private readonly ContosoUniversity.Data.SchoolContext _context;
        private readonly ILogger<InstructorCoursesPageModel> _logger;

        public CreateModel(SchoolContext context,
                          ILogger<InstructorCoursesPageModel> logger)
        {
            _context = context;
            _logger = logger;
        }

        public IActionResult OnGet()
        {
            var instructor = new Instructor();
            instructor.Courses = new List<Course>();

            // 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.Length > 0)
            {
                newInstructor.Courses = new List<Course>();
                // Load collection with one DB call.
                _context.Courses.Load();
            }

            // Add selected Courses courses to the new instructor.
            foreach (var course in selectedCourses)
            {
                var foundCourse = await _context.Courses.FindAsync(int.Parse(course));
                if (foundCourse != null)
                {
                    newInstructor.Courses.Add(foundCourse);
                }
                else
                {
                    _logger.LogWarning("Course {course} not found", course);
                }
            }

            try
            {
                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");
                }
                return RedirectToPage("./Index");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex.Message);
            }

            PopulateAssignedCourseData(_context, newInstructor);
            return Page();
        }
    }
}

Kode sebelumnya:

  • Menambahkan pengelogan untuk pesan peringatan dan kesalahan.

  • LoadPanggilan , yang mengambil semua Kursus dalam satu panggilan database. Untuk koleksi kecil, ini adalah pengoptimalan saat menggunakan FindAsync. FindAsync mengembalikan entitas terlacak tanpa permintaan ke database.

    public async Task<IActionResult> OnPostAsync(string[] selectedCourses)
    {
        var newInstructor = new Instructor();
    
        if (selectedCourses.Length > 0)
        {
            newInstructor.Courses = new List<Course>();
            // Load collection with one DB call.
            _context.Courses.Load();
        }
    
        // Add selected Courses courses to the new instructor.
        foreach (var course in selectedCourses)
        {
            var foundCourse = await _context.Courses.FindAsync(int.Parse(course));
            if (foundCourse != null)
            {
                newInstructor.Courses.Add(foundCourse);
            }
            else
            {
                _logger.LogWarning("Course {course} not found", course);
            }
        }
    
        try
        {
            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");
            }
            return RedirectToPage("./Index");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex.Message);
        }
    
        PopulateAssignedCourseData(_context, newInstructor);
        return Page();
    }
    
  • _context.Instructors.Add(newInstructor) membuat hubungan baru Instructor menggunakan banyak-ke-banyak tanpa secara eksplisit memetakan tabel gabungan. Banyak ke banyak ditambahkan di EF 5.0.

Uji halaman Buat instruktur.

Perbarui halaman Buat Razor Instruktur dengan kode yang mirip dengan halaman Edit:

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

Memperbarui halaman Hapus Instruktur

Perbarui Pages/Instructors/Delete.cshtml.cs dengan kode berikut:

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.Courses)
                .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");
        }
    }
}

Kode sebelumnya membuat perubahan berikut:

  • Menggunakan pemuatan bersemangat untuk Courses properti navigasi. Courses harus disertakan atau tidak dihapus saat instruktur dihapus. Untuk menghindari perlu membacanya, konfigurasikan penghapusan kaskade dalam database.

  • Jika instruktur yang akan dihapus ditetapkan sebagai administrator departemen mana pun, menghapus penugasan instruktur dari departemen tersebut.

Jalankan aplikasi dan uji halaman Hapus.

Langkah berikutnya

Tutorial ini menunjukkan cara memperbarui data terkait. Ilustrasi berikut ini memperlihatkan beberapa halaman yang telah selesai.

Halaman Edit KursusHalaman Edit Instruktur

Memperbarui halaman Buat dan Edit Kursus

Kode perancah untuk halaman Buat dan Edit Kursus memiliki daftar drop-down Departemen yang memperlihatkan ID Departemen (bilangan bulat). Menu drop-down harus menampilkan nama Departemen, sehingga kedua halaman ini memerlukan daftar nama departemen. Untuk menyediakan daftar tersebut, gunakan kelas dasar untuk halaman Buat dan Edit.

Membuat kelas dasar untuk Membuat dan Mengedit Kursus

Buat Pages/Courses/DepartmentNamePageModel.cs file dengan kode berikut:

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

Kode sebelumnya membuat SelectList untuk berisi daftar nama departemen. Jika selectedDepartment ditentukan, departemen tersebut dipilih di SelectList.

Kelas Buat dan Edit model halaman akan berasal dari DepartmentNamePageModel.

Memperbarui model halaman Buat Kursus

Kursus ditugaskan ke Departemen. Kelas dasar untuk halaman Buat dan Edit menyediakan SelectList untuk memilih departemen. Daftar drop-down yang menggunakan SelectList properti set Course.DepartmentID kunci asing (FK). EF CoreCourse.DepartmentID menggunakan FK untuk memuat Department properti navigasi.

Buat kursus

Perbarui Pages/Courses/Create.cshtml.cs dengan kode berikut:

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

Jika Anda ingin melihat komentar kode yang diterjemahkan ke bahasa selain bahasa Inggris, beri tahu kami dalam masalah diskusi GitHub ini.

Kode sebelumnya:

  • Berasal dari DepartmentNamePageModel.
  • TryUpdateModelAsync Menggunakan untuk mencegah overposting.
  • ViewData["DepartmentID"]Menghapus . DepartmentNameSL dari kelas dasar adalah model yang ditik dengan kuat dan akan digunakan oleh Razor halaman. Model yang sangat di ketik lebih disukai daripada yang ditik lemah. Untuk informasi selengkapnya, lihat Data yang di ketik dengan lemah (ViewData dan ViewBag).

Memperbarui halaman Buat Razor Kursus

Perbarui Pages/Courses/Create.cshtml dengan kode berikut:

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

Kode sebelumnya membuat perubahan berikut:

  • Mengubah keterangan dari DepartmentID ke Departemen.
  • "ViewBag.DepartmentID" Mengganti dengan DepartmentNameSL (dari kelas dasar).
  • Menambahkan opsi "Pilih Departemen". Perubahan ini merender "Pilih Departemen" di menu drop-down ketika belum ada departemen yang dipilih, bukan departemen pertama.
  • Menambahkan pesan validasi saat departemen tidak dipilih.

Halaman Razor menggunakan Pembantu Pilih Tag:

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

Uji halaman Buat. Halaman Buat menampilkan nama departemen daripada ID departemen.

Memperbarui model halaman Edit Kursus

Perbarui Pages/Courses/Edit.cshtml.cs dengan kode berikut:

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

Perubahannya mirip dengan yang dibuat dalam model halaman Buat. Dalam kode sebelumnya, PopulateDepartmentsDropDownList meneruskan ID departemen, yang memilih departemen tersebut di daftar drop-down.

Memperbarui halaman Edit Razor Kursus

Perbarui Pages/Courses/Edit.cshtml dengan kode berikut:

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

Kode sebelumnya membuat perubahan berikut:

  • Menampilkan ID kursus. Umumnya Kunci Primer (PK) entitas tidak ditampilkan. PK biasanya tidak berarti bagi pengguna. Dalam hal ini, PK adalah nomor kursus.
  • Mengubah keterangan untuk drop-down Departemen dari DepartmentID ke Departemen.
  • "ViewBag.DepartmentID" Mengganti dengan DepartmentNameSL (dari kelas dasar).

Halaman berisi bidang tersembunyi (<input type="hidden">) untuk nomor kursus. Menambahkan pembantu <label> tag dengan asp-for="Course.CourseID" tidak menghilangkan kebutuhan akan bidang tersembunyi. <input type="hidden"> diperlukan agar nomor kursus disertakan dalam data yang diposting saat pengguna mengklik Simpan.

Memperbarui halaman Detail Kursus dan Hapus

AsNoTracking dapat meningkatkan performa saat pelacakan tidak diperlukan.

Memperbarui model halaman Kursus

Perbarui Pages/Courses/Delete.cshtml.cs dengan kode berikut untuk menambahkan 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");
        }
    }
}

Buat perubahan yang sama dalam Pages/Courses/Details.cshtml.cs file:

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

Memperbarui halaman Kursus Razor

Perbarui Pages/Courses/Delete.cshtml dengan kode berikut:

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

Buat perubahan yang sama pada halaman Detail.

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

Menguji halaman Kursus

Uji halaman buat, edit, detail, dan hapus.

Memperbarui halaman Buat dan Edit instruktur

Instruktur dapat mengajarkan sejumlah kursus. Gambar berikut menunjukkan halaman Edit instruktur dengan array kotak centang kursus.

Halaman Edit Instruktur dengan kursus

Kotak centang mengaktifkan perubahan pada kursus tempat instruktur ditetapkan. Kotak centang ditampilkan untuk setiap kursus dalam database. Kursus yang ditetapkan instruktur dipilih. Pengguna dapat memilih atau menghapus kotak centang untuk mengubah penetapan kursus. Jika jumlah kursus jauh lebih besar, UI yang berbeda mungkin bekerja lebih baik. Tetapi metode mengelola hubungan banyak ke banyak yang ditunjukkan di sini tidak akan berubah. Untuk membuat atau menghapus hubungan, Anda memanipulasi entitas gabungan.

Membuat kelas untuk data kursus yang ditetapkan

Buat Models/SchoolViewModels/AssignedCourseData.cs dengan kode berikut:

namespace ContosoUniversity.Models.SchoolViewModels
{
    public class AssignedCourseData
    {
        public int CourseID { get; set; }
        public string Title { get; set; }
        public bool Assigned { get; set; }
    }
}

Kelas AssignedCourseData berisi data untuk membuat kotak centang untuk kursus yang ditetapkan ke instruktur.

Membuat kelas dasar model halaman Instruktur

Pages/Instructors/InstructorCoursesPageModel.cs Buat kelas dasar:

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 adalah kelas dasar yang akan Anda gunakan untuk model halaman Edit dan Buat. PopulateAssignedCourseData membaca semua Course entitas untuk mengisi AssignedCourseDataList. Untuk setiap kursus, kode menetapkan CourseID, judul, dan apakah instruktur ditetapkan ke kursus atau tidak. HashSet digunakan untuk pencarian yang efisien.

Razor Karena halaman tidak memiliki kumpulan entitas Kursus, pengikat model tidak dapat memperbarui CourseAssignments properti navigasi secara otomatis. Alih-alih menggunakan pengikat model untuk memperbarui CourseAssignments properti navigasi, Anda melakukannya dalam metode baru UpdateInstructorCourses . Oleh karena itu Anda perlu mengecualikan CourseAssignments properti dari pengikatan model. Ini tidak memerlukan perubahan apa pun pada kode yang memanggil TryUpdateModel karena Anda menggunakan kelebihan beban dengan properti yang dideklarasikan dan CourseAssignments tidak ada dalam daftar sertakan.

Jika tidak ada kotak centang yang dipilih, kode dalam UpdateInstructorCourses menginisialisasi CourseAssignments properti navigasi dengan koleksi kosong dan mengembalikan:

if (selectedCourses == null)
{
    instructorToUpdate.CourseAssignments = new List<CourseAssignment>();
    return;
}

Kode kemudian mengulangi semua kursus dalam database dan memeriksa setiap kursus terhadap yang saat ini ditetapkan ke instruktur versus yang dipilih di halaman. Untuk memfasilitasi pencarian yang efisien, dua koleksi terakhir disimpan dalam HashSet objek.

Jika kotak centang untuk kursus dipilih tetapi kursus tidak ada di Instructor.CourseAssignments properti navigasi, kursus ditambahkan ke koleksi di properti navigasi.

if (selectedCoursesHS.Contains(course.CourseID.ToString()))
{
    if (!instructorCourses.Contains(course.CourseID))
    {
        instructorToUpdate.CourseAssignments.Add(
            new CourseAssignment
            {
                InstructorID = instructorToUpdate.ID,
                CourseID = course.CourseID
            });
    }
}

Jika kotak centang untuk kursus tidak dipilih, tetapi kursus berada di Instructor.CourseAssignments properti navigasi, kursus akan dihapus dari properti navigasi.

else
{
    if (instructorCourses.Contains(course.CourseID))
    {
        CourseAssignment courseToRemove
            = instructorToUpdate
                .CourseAssignments
                .SingleOrDefault(i => i.CourseID == course.CourseID);
        context.Remove(courseToRemove);
    }
}

Menangani lokasi kantor

Hubungan lain yang harus ditangani halaman edit adalah hubungan satu-ke-nol-atau-satu yang dimiliki entitas Instruktur dengan OfficeAssignment entitas. Kode edit instruktur harus menangani skenario berikut:

  • Jika pengguna menghapus penetapan kantor, hapus OfficeAssignment entitas.
  • Jika pengguna memasukkan penetapan kantor dan kosong, buat entitas baru OfficeAssignment .
  • Jika pengguna mengubah penetapan office, perbarui OfficeAssignment entitas.

Memperbarui model halaman Edit Instruktur

Perbarui Pages/Instructors/Edit.cshtml.cs dengan kode berikut:

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

Kode sebelumnya:

  • Mendapatkan entitas saat ini Instructor dari database menggunakan pemuatan bersemangat untuk OfficeAssignmentproperti navigasi , , CourseAssignmentdan CourseAssignment.Course .
  • Memperbarui entitas yang diambil Instructor dengan nilai dari pengikat model. TryUpdateModelmencegah overposting.
  • Jika lokasi kantor kosong, atur Instructor.OfficeAssignment ke null. Ketika Instructor.OfficeAssignment null, baris terkait dalam OfficeAssignment tabel dihapus.
  • PopulateAssignedCourseData Panggilan masuk OnGetAsync untuk memberikan informasi untuk kotak centang menggunakan AssignedCourseData kelas model tampilan.
  • UpdateInstructorCourses Panggilan masuk untuk menerapkan informasi dari kotak centang ke entitas Instruktur yang sedang dieditOnPostAsync.
  • PopulateAssignedCourseData Panggilan dan UpdateInstructorCourses masuk OnPostAsync jika TryUpdateModel gagal. Metode ini memanggil pemulihan data kursus yang ditetapkan yang dimasukkan di halaman ketika diputar ulang dengan pesan kesalahan.

Memperbarui halaman Edit Razor Instruktur

Perbarui Pages/Instructors/Edit.cshtml dengan kode berikut:

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

Kode sebelumnya membuat tabel HTML yang memiliki tiga kolom. Setiap kolom memiliki kotak centang dan keterangan yang berisi nomor kursus dan judul. Semua kotak centang memiliki nama yang sama ("selectedCourses"). Menggunakan nama yang sama menginformasikan pengikat model untuk memperlakukannya sebagai grup. Atribut nilai dari setiap kotak centang diatur ke CourseID. Saat halaman diposting, pengikat model meneruskan array yang terdiri dari CourseID nilai hanya untuk kotak centang yang dipilih.

Ketika kotak centang awalnya dirender, kursus yang ditetapkan ke instruktur dipilih.

Catatan: Pendekatan yang diambil di sini untuk mengedit data kursus instruktur berfungsi dengan baik ketika ada sejumlah kursus yang terbatas. Untuk koleksi yang jauh lebih besar, UI yang berbeda dan metode pembaruan yang berbeda akan lebih dapat digunakan dan efisien.

Jalankan aplikasi dan uji halaman Edit Instruktur yang diperbarui. Ubah beberapa tugas kursus. Perubahan tercermin pada halaman Indeks.

Memperbarui halaman Buat Instruktur

Perbarui model dan Razor halaman halaman Buat Instruktur dengan kode yang mirip dengan halaman Edit:

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

Uji halaman Buat instruktur.

Memperbarui halaman Hapus Instruktur

Perbarui Pages/Instructors/Delete.cshtml.cs dengan kode berikut:

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

Kode sebelumnya membuat perubahan berikut:

  • Menggunakan pemuatan bersemangat untuk CourseAssignments properti navigasi. CourseAssignments harus disertakan atau tidak dihapus saat instruktur dihapus. Untuk menghindari perlu membacanya, konfigurasikan penghapusan kaskade dalam database.

  • Jika instruktur yang akan dihapus ditetapkan sebagai administrator departemen mana pun, menghapus penugasan instruktur dari departemen tersebut.

Jalankan aplikasi dan uji halaman Hapus.

Langkah berikutnya

Tutorial ini menunjukkan pembaruan data terkait. Jika Mengalami masalah, Anda tidak dapat menyelesaikan, mengunduh, atau melihat aplikasi yang telah selesai. Unduh instruksi.

Ilustrasi berikut ini memperlihatkan beberapa halaman yang telah selesai.

Halaman Edit KursusHalaman Edit Instruktur

Periksa dan uji halaman kursus Buat dan Edit. Buat kursus baru. Departemen dipilih oleh kunci utamanya (bilangan bulat), bukan namanya. Edit kursus baru. Setelah Anda selesai menguji, hapus kursus baru.

Membuat kelas dasar untuk berbagi kode umum

Halaman Kursus/Buat dan Kursus/Edit masing-masing memerlukan daftar nama departemen. Pages/Courses/DepartmentNamePageModel.cshtml.cs Buat kelas dasar untuk halaman Buat dan Edit:

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

Kode sebelumnya membuat SelectList untuk berisi daftar nama departemen. Jika selectedDepartment ditentukan, departemen tersebut dipilih di SelectList.

Kelas Buat dan Edit model halaman akan berasal dari DepartmentNamePageModel.

Mengkustomisasi Halaman Kursus

Ketika entitas kursus baru dibuat, entitas tersebut harus memiliki hubungan dengan departemen yang ada. Untuk menambahkan departemen saat membuat kursus, kelas dasar untuk Buat dan Edit berisi daftar drop-down untuk memilih departemen. Daftar drop-down mengatur Course.DepartmentID properti kunci asing (FK). EF CoreCourse.DepartmentID menggunakan FK untuk memuat Department properti navigasi.

Buat kursus

Perbarui model halaman Buat dengan kode berikut:

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()
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

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

Kode sebelumnya:

  • Berasal dari DepartmentNamePageModel.
  • TryUpdateModelAsync Menggunakan untuk mencegah overposting.
  • ViewData["DepartmentID"] Mengganti dengan DepartmentNameSL (dari kelas dasar).

ViewData["DepartmentID"] diganti dengan jenis yang kuat DepartmentNameSL. Model yang sangat di ketik lebih disukai daripada yang ditik lemah. Untuk informasi selengkapnya, lihat Data yang di ketik dengan lemah (ViewData dan ViewBag).

Perbarui halaman Buat Kursus

Perbarui Pages/Courses/Create.cshtml dengan kode berikut:

@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-default" />
            </div>
        </form>
    </div>
</div>
<div>
    <a asp-page="Index">Back to List</a>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Markup sebelumnya membuat perubahan berikut:

  • Mengubah keterangan dari DepartmentID ke Departemen.
  • "ViewBag.DepartmentID" Mengganti dengan DepartmentNameSL (dari kelas dasar).
  • Menambahkan opsi "Pilih Departemen". Perubahan ini merender "Pilih Departemen" daripada departemen pertama.
  • Menambahkan pesan validasi saat departemen tidak dipilih.

Halaman Razor menggunakan Pembantu Pilih Tag:

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

Uji halaman Buat. Halaman Buat menampilkan nama departemen daripada ID departemen.

Perbarui halaman Edit Kursus.

Ganti kode di Pages/Courses/Edit.cshtml.cs dengan kode berikut:

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 (!ModelState.IsValid)
            {
                return Page();
            }

            var courseToUpdate = await _context.Courses.FindAsync(id);

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

Perubahannya mirip dengan yang dibuat dalam model halaman Buat. Dalam kode sebelumnya, PopulateDepartmentsDropDownList meneruskan ID departemen, yang memilih departemen yang ditentukan dalam daftar drop-down.

Perbarui Pages/Courses/Edit.cshtml dengan markup berikut:

@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-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Markup sebelumnya membuat perubahan berikut:

  • Menampilkan ID kursus. Umumnya Kunci Primer (PK) entitas tidak ditampilkan. PK biasanya tidak berarti bagi pengguna. Dalam hal ini, PK adalah nomor kursus.
  • Mengubah keterangan dari DepartmentID ke Departemen.
  • "ViewBag.DepartmentID" Mengganti dengan DepartmentNameSL (dari kelas dasar).

Halaman berisi bidang tersembunyi (<input type="hidden">) untuk nomor kursus. Menambahkan pembantu <label> tag dengan asp-for="Course.CourseID" tidak menghilangkan kebutuhan akan bidang tersembunyi. <input type="hidden"> diperlukan agar nomor kursus disertakan dalam data yang diposting saat pengguna mengklik Simpan.

Uji kode yang diperbarui. Membuat, mengedit, dan menghapus kursus.

Menambahkan AsNoTracking ke model halaman Detail dan Hapus

AsNoTracking dapat meningkatkan performa saat pelacakan tidak diperlukan. Tambahkan AsNoTracking ke model halaman Hapus dan Detail. Kode berikut menunjukkan model halaman Hapus yang diperbarui:

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
            .AsNoTracking()
            .FirstOrDefaultAsync(m => m.CourseID == id);

        if (Course != null)
        {
            _context.Courses.Remove(Course);
            await _context.SaveChangesAsync();
        }

        return RedirectToPage("./Index");
    }
}

OnGetAsync Perbarui metode dalam Pages/Courses/Details.cshtml.cs file:

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

Mengubah halaman Hapus dan Detail

Perbarui halaman Hapus Razor dengan markup berikut:

@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="dl-horizontal">
        <dt>
            @Html.DisplayNameFor(model => model.Course.CourseID)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Course.CourseID)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Course.Title)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Course.Title)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Course.Credits)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Course.Credits)
        </dd>
        <dt>
            @Html.DisplayNameFor(model => model.Course.Department)
        </dt>
        <dd>
            @Html.DisplayFor(model => model.Course.Department.DepartmentID)
        </dd>
    </dl>
    
    <form method="post">
        <input type="hidden" asp-for="Course.CourseID" />
        <input type="submit" value="Delete" class="btn btn-default" /> |
        <a asp-page="./Index">Back to List</a>
    </form>
</div>

Buat perubahan yang sama pada halaman Detail.

Menguji halaman Kursus

Uji buat, edit, detail, dan hapus.

Memperbarui halaman instruktur

Bagian berikut memperbarui halaman instruktur.

Menambahkan lokasi kantor

Saat mengedit catatan instruktur, Anda mungkin ingin memperbarui penetapan kantor instruktur. Entitas Instructor memiliki hubungan satu-ke-nol-atau-satu dengan OfficeAssignment entitas. Kode instruktur harus menangani:

  • Jika pengguna menghapus penetapan kantor, hapus OfficeAssignment entitas.
  • Jika pengguna memasukkan penetapan kantor dan kosong, buat entitas baru OfficeAssignment .
  • Jika pengguna mengubah penetapan office, perbarui OfficeAssignment entitas.

Perbarui model halaman Edit instruktur dengan kode berikut:

public class EditModel : PageModel
{
    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)
            .AsNoTracking()
            .FirstOrDefaultAsync(m => m.ID == id);

        if (Instructor == null)
        {
            return NotFound();
        }
        return Page();
    }

    public async Task<IActionResult> OnPostAsync(int? id)
    {
        if (!ModelState.IsValid)
        {
            return Page();
        }

        var instructorToUpdate = await _context.Instructors
            .Include(i => i.OfficeAssignment)
            .FirstOrDefaultAsync(s => s.ID == id);

        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;
            }
            await _context.SaveChangesAsync();
        }
        return RedirectToPage("./Index");

    }
}

Kode sebelumnya:

  • Mendapatkan entitas saat ini Instructor dari database menggunakan pemuatan bersemangat untuk OfficeAssignment properti navigasi.
  • Memperbarui entitas yang diambil Instructor dengan nilai dari pengikat model. TryUpdateModelmencegah overposting.
  • Jika lokasi kantor kosong, atur Instructor.OfficeAssignment ke null. Ketika Instructor.OfficeAssignment null, baris terkait dalam OfficeAssignment tabel dihapus.

Memperbarui halaman Edit instruktur

Perbarui Pages/Instructors/Edit.cshtml dengan lokasi kantor:

@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">
                <input type="submit" value="Save" class="btn btn-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Verifikasi bahwa Anda dapat mengubah lokasi kantor instruktur.

Menambahkan tugas Kursus ke halaman Edit instruktur

Instruktur dapat mengajarkan sejumlah kursus. Di bagian ini, Anda menambahkan kemampuan untuk mengubah tugas kursus. Gambar berikut menunjukkan halaman Edit instruktur yang diperbarui:

Halaman Edit Instruktur dengan kursus

Course dan Instructor memiliki hubungan banyak ke banyak. Untuk menambahkan dan menghapus hubungan, Anda menambahkan dan menghapus entitas dari CourseAssignments kumpulan entitas gabungan.

kotak centang mengaktifkan perubahan pada kursus yang ditetapkan instruktur. Kotak centang ditampilkan untuk setiap kursus dalam database. Kursus yang ditetapkan instruktur diperiksa. Pengguna dapat memilih atau menghapus kotak centang untuk mengubah penetapan kursus. Jika jumlah kursus jauh lebih besar:

  • Anda mungkin akan menggunakan antarmuka pengguna yang berbeda untuk menampilkan kursus.
  • Metode memanipulasi entitas gabungan untuk membuat atau menghapus hubungan tidak akan berubah.

Menambahkan kelas untuk mendukung halaman Instruktur Buat dan Edit

Buat Models/SchoolViewModels/AssignedCourseData.cs dengan kode berikut:

namespace ContosoUniversity.Models.SchoolViewModels
{
    public class AssignedCourseData
    {
        public int CourseID { get; set; }
        public string Title { get; set; }
        public bool Assigned { get; set; }
    }
}

Kelas AssignedCourseData berisi data untuk membuat kotak centang untuk kursus yang ditetapkan oleh instruktur.

Pages/Instructors/InstructorCoursesPageModel.cshtml.cs Buat kelas dasar:

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 adalah kelas dasar yang akan Anda gunakan untuk model halaman Edit dan Buat. PopulateAssignedCourseData membaca semua Course entitas untuk mengisi AssignedCourseDataList. Untuk setiap kursus, kode menetapkan CourseID, judul, dan apakah instruktur ditetapkan ke kursus atau tidak. HashSet digunakan untuk membuat pencarian yang efisien.

Model halaman Edit Instruktur

Perbarui model halaman Edit instruktur dengan kode berikut:

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 (!ModelState.IsValid)
        {
            return Page();
        }

        var instructorToUpdate = await _context.Instructors
            .Include(i => i.OfficeAssignment)
            .Include(i => i.CourseAssignments)
                .ThenInclude(i => i.Course)
            .FirstOrDefaultAsync(s => s.ID == id);

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

Kode sebelumnya menangani perubahan penetapan office.

Perbarui Tampilan instruktur Razor :

@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="col-md-offset-2 col-md-10">
                    <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-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="./Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Catatan

Saat Anda menempelkan kode di Visual Studio, hentian baris diubah dengan cara yang merusak kode. Tekan Ctrl+Z satu kali untuk mengurungkan pemformatan otomatis. Ctrl+Z memperbaiki hentian baris sehingga terlihat seperti yang Anda lihat di sini. Indentasi tidak harus sempurna, tetapi @:</tr><tr>baris , , @:<td>@:</td>, dan @:</tr> masing-masing harus berada di satu baris seperti yang ditunjukkan. Dengan blok kode baru dipilih, tekan Tab tiga kali untuk mengjajarkan kode baru dengan kode yang ada. Pilih atau tinjau status bug ini dengan tautan ini.

Kode sebelumnya membuat tabel HTML yang memiliki tiga kolom. Setiap kolom memiliki kotak centang dan keterangan yang berisi nomor kursus dan judul. Semua kotak centang memiliki nama yang sama ("selectedCourses"). Menggunakan nama yang sama menginformasikan pengikat model untuk memperlakukannya sebagai grup. Atribut nilai dari setiap kotak centang diatur ke CourseID. Saat halaman diposting, pengikat model meneruskan array yang terdiri dari CourseID nilai hanya untuk kotak centang yang dipilih.

Ketika kotak centang awalnya dirender, kursus yang ditetapkan ke instruktur telah memeriksa atribut.

Jalankan aplikasi dan uji halaman Edit instruktur yang diperbarui. Ubah beberapa tugas kursus. Perubahan tercermin pada halaman Indeks.

Catatan: Pendekatan yang diambil di sini untuk mengedit data kursus instruktur berfungsi dengan baik ketika ada sejumlah kursus yang terbatas. Untuk koleksi yang jauh lebih besar, UI yang berbeda dan metode pembaruan yang berbeda akan lebih dapat digunakan dan efisien.

Memperbarui halaman Buat instruktur

Perbarui model halaman Buat instruktur dengan kode berikut:

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)
        {
            if (!ModelState.IsValid)
            {
                return Page();
            }

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

Kode sebelumnya mirip Pages/Instructors/Edit.cshtml.cs dengan kode.

Perbarui halaman Buat Razor instruktur dengan markup berikut:

@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="col-md-offset-2 col-md-10">
                    <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-default" />
            </div>
        </form>
    </div>
</div>

<div>
    <a asp-page="Index">Back to List</a>
</div>

@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
}

Uji halaman Buat instruktur.

Memperbarui halaman Hapus

Perbarui model halaman Hapus dengan kode berikut:

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.SingleAsync(m => m.ID == id);

            if (Instructor == null)
            {
                return NotFound();
            }
            return Page();
        }

        public async Task<IActionResult> OnPostAsync(int id)
        {
            Instructor instructor = await _context.Instructors
                .Include(i => i.CourseAssignments)
                .SingleAsync(i => i.ID == id);

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

Kode sebelumnya membuat perubahan berikut:

  • Menggunakan pemuatan bersemangat untuk CourseAssignments properti navigasi. CourseAssignments harus disertakan atau tidak dihapus saat instruktur dihapus. Untuk menghindari perlu membacanya, konfigurasikan penghapusan kaskade dalam database.

  • Jika instruktur yang akan dihapus ditetapkan sebagai administrator departemen mana pun, menghapus penugasan instruktur dari departemen tersebut.

Sumber Daya Tambahan: