Tutorial: Memperbarui data terkait - ASP.NET MVC dengan EF Core
Dalam tutorial sebelumnya Anda menampilkan data terkait; dalam tutorial ini Anda akan memperbarui data terkait dengan memperbarui bidang kunci asing dan properti navigasi.
Ilustrasi berikut ini memperlihatkan beberapa halaman yang akan Anda kerjakan.
Di tutorial ini, Anda akan:
- Mengkustomisasi halaman Kursus
- Tambahkan halaman Edit Instruktur
- Menambahkan kursus ke halaman Edit
- Perbarui halaman Hapus
- Menambahkan lokasi dan kursus office ke halaman Buat
Prasyarat
Mengkustomisasi halaman Kursus
Ketika entitas baru Course
dibuat, entitas harus memiliki hubungan dengan departemen yang ada. Untuk memfasilitasi hal ini, kode perancah mencakup metode pengontrol dan Membuat dan Mengedit tampilan yang menyertakan daftar drop-down untuk memilih departemen. Daftar drop-down mengatur Course.DepartmentID
properti kunci asing, dan itu semua yang dibutuhkan Kerangka Kerja Entitas untuk memuat Department
properti navigasi dengan entitas yang sesuai Department
. Anda akan menggunakan kode perancah, tetapi mengubahnya sedikit untuk menambahkan penanganan kesalahan dan mengurutkan daftar drop-down.
Di CoursesController.cs
, hapus empat metode Buat dan Edit dan ganti dengan kode berikut:
public IActionResult Create()
{
PopulateDepartmentsDropDownList();
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("CourseID,Credits,DepartmentID,Title")] Course course)
{
if (ModelState.IsValid)
{
_context.Add(course);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var course = await _context.Courses
.AsNoTracking()
.FirstOrDefaultAsync(m => m.CourseID == id);
if (course == null)
{
return NotFound();
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
if (id == null)
{
return NotFound();
}
var courseToUpdate = await _context.Courses
.FirstOrDefaultAsync(c => c.CourseID == id);
if (await TryUpdateModelAsync<Course>(courseToUpdate,
"",
c => c.Credits, c => c.DepartmentID, c => c.Title))
{
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
return RedirectToAction(nameof(Index));
}
PopulateDepartmentsDropDownList(courseToUpdate.DepartmentID);
return View(courseToUpdate);
}
Edit
Setelah metode HttpPost, buat metode baru yang memuat info departemen untuk daftar drop-down.
private void PopulateDepartmentsDropDownList(object selectedDepartment = null)
{
var departmentsQuery = from d in _context.Departments
orderby d.Name
select d;
ViewBag.DepartmentID = new SelectList(departmentsQuery.AsNoTracking(), "DepartmentID", "Name", selectedDepartment);
}
Metode ini PopulateDepartmentsDropDownList
mendapatkan daftar semua departemen yang diurutkan menurut nama, membuat SelectList
koleksi untuk daftar drop-down, dan meneruskan koleksi ke tampilan di ViewBag
. Metode ini menerima parameter opsional selectedDepartment
yang memungkinkan kode panggilan menentukan item yang akan dipilih saat daftar drop-down dirender. Tampilan akan meneruskan nama "DepartmentID" ke <select>
pembantu tag, dan pembantu kemudian tahu untuk melihat ViewBag
objek untuk SelectList
bernama "DepartmentID".
Metode HttpGet Create
memanggil PopulateDepartmentsDropDownList
metode tanpa mengatur item yang dipilih, karena untuk kursus baru departemen belum ditetapkan:
public IActionResult Create()
{
PopulateDepartmentsDropDownList();
return View();
}
Metode HttpGet Edit
mengatur item yang dipilih, berdasarkan ID departemen yang sudah ditetapkan ke kursus yang sedang diedit:
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var course = await _context.Courses
.AsNoTracking()
.FirstOrDefaultAsync(m => m.CourseID == id);
if (course == null)
{
return NotFound();
}
PopulateDepartmentsDropDownList(course.DepartmentID);
return View(course);
}
Metode HttpPost untuk keduanya Create
dan Edit
juga menyertakan kode yang mengatur item yang dipilih saat mereka memutar ulang halaman setelah kesalahan. Ini memastikan bahwa ketika halaman diputar ulang untuk menampilkan pesan kesalahan, departemen apa pun yang dipilih tetap dipilih.
Menambahkan. AsNoTracking ke Metode Detail dan Hapus
Untuk mengoptimalkan performa halaman Detail Kursus dan Hapus, tambahkan AsNoTracking
panggilan dalam Details
metode HttpGet Delete
dan .
public async Task<IActionResult> Details(int? id)
{
if (id == null)
{
return NotFound();
}
var course = await _context.Courses
.Include(c => c.Department)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.CourseID == id);
if (course == null)
{
return NotFound();
}
return View(course);
}
public async Task<IActionResult> Delete(int? id)
{
if (id == null)
{
return NotFound();
}
var course = await _context.Courses
.Include(c => c.Department)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.CourseID == id);
if (course == null)
{
return NotFound();
}
return View(course);
}
Mengubah tampilan Kursus
Di Views/Courses/Create.cshtml
, tambahkan opsi "Pilih Departemen" ke daftar drop-down Departemen , ubah keterangan dari DepartmentID ke Departemen, dan tambahkan pesan validasi.
<div class="form-group">
<label asp-for="Department" class="control-label"></label>
<select asp-for="DepartmentID" class="form-control" asp-items="ViewBag.DepartmentID">
<option value="">-- Select Department --</option>
</select>
<span asp-validation-for="DepartmentID" class="text-danger" />
</div>
Dalam Views/Courses/Edit.cshtml
, buat perubahan yang sama untuk bidang Departemen yang baru saja Anda lakukan di Create.cshtml
.
Juga di Views/Courses/Edit.cshtml
, tambahkan bidang nomor kursus sebelum bidang Judul . Karena nomor kursus adalah kunci utama, itu ditampilkan, tetapi tidak dapat diubah.
<div class="form-group">
<label asp-for="CourseID" class="control-label"></label>
<div>@Html.DisplayFor(model => model.CourseID)</div>
</div>
Sudah ada bidang tersembunyi (<input type="hidden">
) untuk nomor kursus dalam tampilan Edit. Menambahkan pembantu <label>
tag tidak menghilangkan kebutuhan akan bidang tersembunyi karena tidak menyebabkan nomor kursus disertakan dalam data yang diposting saat pengguna mengklik Simpan di halaman Edit .
Di Views/Courses/Delete.cshtml
, tambahkan bidang nomor kursus di bagian atas dan ubah ID departemen menjadi nama departemen.
@model ContosoUniversity.Models.Course
@{
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.CourseID)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.CourseID)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Title)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Title)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Credits)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Credits)
</dd>
<dt class="col-sm-2">
@Html.DisplayNameFor(model => model.Department)
</dt>
<dd class="col-sm-10">
@Html.DisplayFor(model => model.Department.Name)
</dd>
</dl>
<form asp-action="Delete">
<div class="form-actions no-color">
<input type="submit" value="Delete" class="btn btn-default" /> |
<a asp-action="Index">Back to List</a>
</div>
</form>
</div>
Dalam Views/Courses/Details.cshtml
, buat perubahan yang sama yang baru saja Anda lakukan untuk Delete.cshtml
.
Menguji halaman Kursus
Jalankan aplikasi, pilih tab Kursus , klik Buat Baru, dan masukkan data untuk kursus baru:
Klik Buat. Halaman Indeks Kursus ditampilkan dengan kursus baru ditambahkan ke daftar. Nama departemen dalam daftar halaman Indeks berasal dari properti navigasi, memperlihatkan bahwa hubungan dibuat dengan benar.
Klik Edit pada kursus di halaman Indeks Kursus.
Ubah data di halaman dan klik Simpan. Halaman Indeks Kursus ditampilkan dengan data kursus yang diperbarui.
Tambahkan halaman Edit Instruktur
Saat mengedit catatan instruktur, Anda ingin dapat memperbarui penetapan kantor instruktur. Entitas Instructor
memiliki hubungan satu-ke-nol-atau-satu dengan OfficeAssignment
entitas, yang berarti kode Anda harus menangani situasi berikut:
Jika pengguna menghapus penetapan office dan awalnya memiliki nilai, hapus
OfficeAssignment
entitas.Jika pengguna memasukkan nilai penetapan office dan awalnya kosong, buat entitas baru
OfficeAssignment
.Jika pengguna mengubah nilai penetapan office, ubah nilai di entitas yang sudah ada
OfficeAssignment
.
Memperbarui pengontrol Instruktur
Di InstructorsController.cs
, ubah kode dalam metode HttpGet Edit
sehingga memuat properti navigasi dan panggilan AsNoTracking
entitas OfficeAssignment
Instruktur :
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var instructor = await _context.Instructors
.Include(i => i.OfficeAssignment)
.AsNoTracking()
.FirstOrDefaultAsync(m => m.ID == id);
if (instructor == null)
{
return NotFound();
}
return View(instructor);
}
Ganti metode HttpPost Edit
dengan kode berikut untuk menangani pembaruan penetapan office:
[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
if (id == null)
{
return NotFound();
}
var instructorToUpdate = await _context.Instructors
.Include(i => i.OfficeAssignment)
.FirstOrDefaultAsync(s => s.ID == id);
if (await TryUpdateModelAsync<Instructor>(
instructorToUpdate,
"",
i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
{
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
{
instructorToUpdate.OfficeAssignment = null;
}
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
return RedirectToAction(nameof(Index));
}
return View(instructorToUpdate);
}
Kode melakukan hal berikut:
Mengubah nama metode menjadi
EditPost
karena tanda tangan sekarang sama dengan metode HttpGetEdit
(ActionName
atribut menentukan bahwa/Edit/
URL masih digunakan).Mendapatkan entitas saat ini
Instructor
dari database menggunakan pemuatan bersemangat untukOfficeAssignment
properti navigasi. Ini sama dengan apa yang Anda lakukan dalam metode HttpGetEdit
.Memperbarui entitas yang diambil
Instructor
dengan nilai dari pengikat model. KelebihanTryUpdateModel
beban memungkinkan Anda mendeklarasikan properti yang ingin Anda sertakan. Ini mencegah pengiriman berlebihan, seperti yang dijelaskan dalam tutorial kedua.if (await TryUpdateModelAsync<Instructor>( instructorToUpdate, "", i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
Jika lokasi kantor kosong, atur
Instructor.OfficeAssignment
properti ke null sehingga baris terkait dalamOfficeAssignment
tabel akan dihapus.if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location)) { instructorToUpdate.OfficeAssignment = null; }
Menyimpan perubahan ke database.
Memperbarui tampilan Edit Instruktur
Di Views/Instructors/Edit.cshtml
, tambahkan bidang baru untuk mengedit lokasi kantor, di akhir sebelum tombol Simpan :
<div class="form-group">
<label asp-for="OfficeAssignment.Location" class="control-label"></label>
<input asp-for="OfficeAssignment.Location" class="form-control" />
<span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
</div>
Jalankan aplikasi, pilih tab Instruktur , lalu klik Edit pada instruktur. Ubah Lokasi Office dan klik Simpan.
Menambahkan kursus ke halaman Edit
Instruktur dapat mengajarkan sejumlah kursus. Sekarang Anda akan meningkatkan halaman Edit Instruktur dengan menambahkan kemampuan untuk mengubah penetapan kursus menggunakan sekelompok kotak centang, seperti yang ditunjukkan dalam cuplikan layar berikut:
Hubungan antara Course
entitas dan Instructor
adalah banyak-ke-banyak. Untuk menambahkan dan menghapus hubungan, Anda menambahkan dan menghapus entitas ke dan dari CourseAssignments
kumpulan entitas gabungan.
UI yang memungkinkan Anda mengubah kursus mana yang ditetapkan instruktur adalah sekelompok kotak centang. Kotak centang untuk setiap kursus dalam database ditampilkan, dan kotak centang yang saat ini ditetapkan instruktur dipilih. Pengguna dapat memilih atau menghapus kotak centang untuk mengubah penetapan kursus. Jika jumlah kursus jauh lebih besar, Anda mungkin ingin menggunakan metode yang berbeda untuk menyajikan data dalam tampilan, tetapi Anda akan menggunakan metode yang sama untuk memanipulasi entitas gabungan untuk membuat atau menghapus hubungan.
Memperbarui pengontrol Instruktur
Untuk menyediakan data ke tampilan untuk daftar kotak centang, Anda akan menggunakan kelas model tampilan.
Buat AssignedCourseData.cs
di folder SchoolViewModels dan ganti kode yang ada dengan kode berikut:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace ContosoUniversity.Models.SchoolViewModels
{
public class AssignedCourseData
{
public int CourseID { get; set; }
public string Title { get; set; }
public bool Assigned { get; set; }
}
}
Di InstructorsController.cs
, ganti metode HttpGet Edit
dengan kode berikut. Perubahan disorot.
public async Task<IActionResult> Edit(int? id)
{
if (id == null)
{
return NotFound();
}
var 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(instructor);
return View(instructor);
}
private void PopulateAssignedCourseData(Instructor instructor)
{
var allCourses = _context.Courses;
var instructorCourses = new HashSet<int>(instructor.CourseAssignments.Select(c => c.CourseID));
var viewModel = new List<AssignedCourseData>();
foreach (var course in allCourses)
{
viewModel.Add(new AssignedCourseData
{
CourseID = course.CourseID,
Title = course.Title,
Assigned = instructorCourses.Contains(course.CourseID)
});
}
ViewData["Courses"] = viewModel;
}
Kode menambahkan pemuatan bersemangat untuk Courses
properti navigasi dan memanggil metode baru PopulateAssignedCourseData
untuk memberikan informasi untuk array kotak centang menggunakan AssignedCourseData
kelas model tampilan.
Kode dalam PopulateAssignedCourseData
metode membaca semua Course
entitas untuk memuat daftar kursus menggunakan kelas model tampilan. Untuk setiap kursus, kode memeriksa apakah kursus ada di properti navigasi instruktur Courses
. Untuk membuat pencarian yang efisien saat memeriksa apakah kursus ditetapkan ke instruktur, kursus yang ditetapkan ke instruktur dimasukkan ke dalam HashSet
koleksi. Properti Assigned
diatur ke true untuk kursus yang ditetapkan instruktur. Tampilan akan menggunakan properti ini untuk menentukan kotak centang mana yang harus ditampilkan seperti yang dipilih. Akhirnya, daftar diteruskan ke tampilan di ViewData
.
Selanjutnya, tambahkan kode yang dijalankan saat pengguna mengklik Simpan. EditPost
Ganti metode dengan kode berikut, dan tambahkan metode baru yang memperbarui Courses
properti navigasi entitas Instruktur.
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Edit(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(m => m.ID == id);
if (await TryUpdateModelAsync<Instructor>(
instructorToUpdate,
"",
i => i.FirstMidName, i => i.LastName, i => i.HireDate, i => i.OfficeAssignment))
{
if (String.IsNullOrWhiteSpace(instructorToUpdate.OfficeAssignment?.Location))
{
instructorToUpdate.OfficeAssignment = null;
}
UpdateInstructorCourses(selectedCourses, instructorToUpdate);
try
{
await _context.SaveChangesAsync();
}
catch (DbUpdateException /* ex */)
{
//Log the error (uncomment ex variable name and write a log.)
ModelState.AddModelError("", "Unable to save changes. " +
"Try again, and if the problem persists, " +
"see your system administrator.");
}
return RedirectToAction(nameof(Index));
}
UpdateInstructorCourses(selectedCourses, instructorToUpdate);
PopulateAssignedCourseData(instructorToUpdate);
return View(instructorToUpdate);
}
private void UpdateInstructorCourses(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.FirstOrDefault(i => i.CourseID == course.CourseID);
_context.Remove(courseToRemove);
}
}
}
}
Tanda tangan metode sekarang berbeda dari metode HttpGet Edit
, sehingga nama metode berubah dari EditPost
kembali ke Edit
.
Karena tampilan 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 yang memerlukan persetujuan eksplisit 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:
private void UpdateInstructorCourses(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.FirstOrDefault(i => i.CourseID == course.CourseID);
_context.Remove(courseToRemove);
}
}
}
}
Kode kemudian mengulangi semua kursus dalam database dan memeriksa setiap kursus terhadap yang saat ini ditetapkan ke instruktur versus yang dipilih dalam tampilan. 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.
private void UpdateInstructorCourses(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.FirstOrDefault(i => i.CourseID == course.CourseID);
_context.Remove(courseToRemove);
}
}
}
}
Jika kotak centang untuk kursus tidak dipilih, tetapi kursus berada di Instructor.CourseAssignments
properti navigasi, kursus akan dihapus dari properti navigasi.
private void UpdateInstructorCourses(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.FirstOrDefault(i => i.CourseID == course.CourseID);
_context.Remove(courseToRemove);
}
}
}
}
Memperbarui tampilan Instruktur
Di Views/Instructors/Edit.cshtml
, tambahkan bidang Kursus dengan array kotak centang dengan menambahkan kode berikut segera setelah div
elemen untuk bidang Office dan sebelum div
elemen untuk tombol Simpan .
Catatan
Saat Anda menempelkan kode di Visual Studio, hentian baris dapat diubah dengan cara yang merusak kode. Jika kode terlihat berbeda setelah menempelkan, tekan Ctrl+Z satu kali untuk membatalkan pemformatan otomatis. Ini akan memperbaiki jeda baris sehingga terlihat seperti apa 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 atau Anda akan mendapatkan kesalahan runtime. Dengan blok kode baru dipilih, tekan Tab tiga kali untuk mengjajarkan kode baru dengan kode yang ada. Masalah ini diperbaiki di Visual Studio 2019.
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<table>
<tr>
@{
int cnt = 0;
List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses;
foreach (var course in courses)
{
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>
Kode ini membuat tabel HTML yang memiliki tiga kolom. Di setiap kolom adalah kotak centang diikuti dengan keterangan yang terdiri dari nomor kursus dan judul. Semua kotak centang memiliki nama yang sama ("selectedCourses"), yang menginformasikan binder model bahwa mereka akan diperlakukan sebagai grup. Atribut nilai dari setiap kotak centang diatur ke nilai CourseID
. Saat halaman diposting, pengikat model meneruskan array ke pengontrol yang terdiri dari CourseID
nilai hanya untuk kotak centang yang dipilih.
Ketika kotak centang awalnya dirender, kotak centang yang untuk kursus yang ditetapkan ke instruktur telah memeriksa atribut, yang memilihnya (menampilkannya dicentang).
Jalankan aplikasi, pilih tab Instruktur , dan klik Edit pada instruktur untuk melihat halaman Edit .
Ubah beberapa tugas kursus dan klik Simpan. Perubahan yang Anda buat 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 diperlukan.
Perbarui halaman Hapus
Di InstructorsController.cs
, hapus DeleteConfirmed
metode dan sisipkan kode berikut di tempatnya.
[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(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 RedirectToAction(nameof(Index));
}
Pembaruan kode ini membuat perubahan berikut:
Apakah pemuatan bersemangat
CourseAssignments
untuk properti navigasi. Anda harus menyertakan ini atau EF tidak akan tahu tentang entitas terkaitCourseAssignment
dan tidak akan menghapusnya. Untuk menghindari perlu membacanya di sini, Anda dapat mengonfigurasi penghapusan kaskade dalam database.Jika instruktur yang akan dihapus ditetapkan sebagai administrator departemen mana pun, menghapus penugasan instruktur dari departemen tersebut.
Menambahkan lokasi dan kursus office ke halaman Buat
Di InstructorsController.cs
, hapus metode HttpGet dan HttpPost Create
, lalu tambahkan kode berikut di tempatnya:
public IActionResult Create()
{
var instructor = new Instructor();
instructor.CourseAssignments = new List<CourseAssignment>();
PopulateAssignedCourseData(instructor);
return View();
}
// POST: Instructors/Create
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create([Bind("FirstMidName,HireDate,LastName,OfficeAssignment")] Instructor instructor, string[] selectedCourses)
{
if (selectedCourses != null)
{
instructor.CourseAssignments = new List<CourseAssignment>();
foreach (var course in selectedCourses)
{
var courseToAdd = new CourseAssignment { InstructorID = instructor.ID, CourseID = int.Parse(course) };
instructor.CourseAssignments.Add(courseToAdd);
}
}
if (ModelState.IsValid)
{
_context.Add(instructor);
await _context.SaveChangesAsync();
return RedirectToAction(nameof(Index));
}
PopulateAssignedCourseData(instructor);
return View(instructor);
}
Kode ini mirip dengan apa yang Anda lihat untuk Edit
metode kecuali bahwa awalnya tidak ada kursus yang dipilih. Metode HttpGet Create
memanggil PopulateAssignedCourseData
metode bukan karena mungkin ada kursus yang dipilih tetapi untuk menyediakan koleksi kosong untuk foreach
perulangan dalam tampilan (jika tidak, kode tampilan akan melemparkan pengecualian referensi null).
Metode HttpPost Create
menambahkan setiap kursus yang dipilih ke CourseAssignments
properti navigasi sebelum memeriksa kesalahan validasi dan menambahkan instruktur baru ke database. Kursus ditambahkan bahkan jika ada kesalahan model sehingga ketika ada kesalahan model (misalnya, pengguna memasukkan tanggal yang tidak valid), dan halaman diputar ulang dengan pesan kesalahan, pilihan kursus apa pun yang dibuat dipulihkan secara otomatis.
Perhatikan bahwa untuk dapat menambahkan kursus ke CourseAssignments
properti navigasi, Anda harus menginisialisasi properti sebagai koleksi kosong:
instructor.CourseAssignments = new List<CourseAssignment>();
Sebagai alternatif untuk melakukan ini dalam kode pengontrol, Anda dapat melakukannya dalam Instructor
model dengan mengubah getter properti untuk membuat koleksi secara otomatis jika tidak ada, seperti yang ditunjukkan dalam contoh berikut:
private ICollection<CourseAssignment> _courseAssignments;
public ICollection<CourseAssignment> CourseAssignments
{
get
{
return _courseAssignments ?? (_courseAssignments = new List<CourseAssignment>());
}
set
{
_courseAssignments = value;
}
}
Jika Anda mengubah properti dengan CourseAssignments
cara ini, Anda dapat menghapus kode inisialisasi properti eksplisit di pengontrol.
Di Views/Instructor/Create.cshtml
, tambahkan kotak teks lokasi office dan kotak centang untuk kursus sebelum tombol Kirim. Seperti dalam kasus halaman Edit, perbaiki pemformatan jika Visual Studio memformat ulang kode saat Anda menempelkannya.
<div class="form-group">
<label asp-for="OfficeAssignment.Location" class="control-label"></label>
<input asp-for="OfficeAssignment.Location" class="form-control" />
<span asp-validation-for="OfficeAssignment.Location" class="text-danger" />
</div>
<div class="form-group">
<div class="col-md-offset-2 col-md-10">
<table>
<tr>
@{
int cnt = 0;
List<ContosoUniversity.Models.SchoolViewModels.AssignedCourseData> courses = ViewBag.Courses;
foreach (var course in courses)
{
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>
Uji dengan menjalankan aplikasi dan membuat instruktur.
Menangani Transaksi
Seperti yang dijelaskan dalam tutorial CRUD, Kerangka Kerja Entitas secara implisit menerapkan transaksi. Untuk skenario di mana Anda memerlukan lebih banyak kontrol -- misalnya, jika Anda ingin menyertakan operasi yang dilakukan di luar Kerangka Kerja Entitas dalam transaksi -- lihat Transaksi.
Mendapatkan kode
Unduh atau lihat aplikasi yang telah selesai.
Langkah berikutnya
Di tutorial ini, Anda akan:
- Halaman Kursus yang Dikustomisasi
- Menambahkan halaman Edit Instruktur
- Menambahkan kursus ke halaman Edit
- Halaman Hapus yang Diperbarui
- Menambahkan lokasi dan kursus office ke halaman Buat
Lanjutkan ke tutorial berikutnya untuk mempelajari cara menangani konflik konkurensi.
ASP.NET Core
Saran dan Komentar
https://aka.ms/ContentUserFeedback.
Segera hadir: Sepanjang tahun 2024 kami akan menghentikan penggunaan GitHub Issues sebagai mekanisme umpan balik untuk konten dan menggantinya dengan sistem umpan balik baru. Untuk mengetahui informasi selengkapnya, lihat:Kirim dan lihat umpan balik untuk