Tutorial: Menerapkan Fungsionalitas CRUD - ASP.NET MVC dengan EF Core

Dalam tutorial sebelumnya, Anda membuat aplikasi MVC yang menyimpan dan menampilkan data menggunakan Kerangka Kerja Entitas dan SQL Server LocalDB. Dalam tutorial ini, Anda akan meninjau dan menyesuaikan kode CRUD (buat, baca, perbarui, hapus) yang dibuat perancah MVC secara otomatis untuk Anda dalam pengontrol dan tampilan.

Catatan

Ini adalah praktik umum untuk menerapkan pola repositori untuk membuat lapisan abstraksi antara pengontrol Anda dan lapisan akses data. Untuk menjaga tutorial ini tetap sederhana dan berfokus pada pengajaran cara menggunakan Kerangka Kerja Entitas itu sendiri, mereka tidak menggunakan repositori. Untuk informasi tentang repositori dengan EF, lihat tutorial terakhir dalam seri ini.

Di tutorial ini, Anda akan:

  • Mengkustomisasi halaman Detail
  • Memperbarui halaman Buat
  • Memperbarui halaman Edit
  • Memperbarui halaman Hapus
  • Menutup koneksi database

Prasyarat

Mengkustomisasi halaman Detail

Kode perancah untuk halaman Indeks Siswa meninggalkan Enrollments properti , karena properti tersebut menyimpan koleksi. Di halaman Detail , Anda akan menampilkan konten koleksi dalam tabel HTML.

Dalam Controllers/StudentsController.cs, metode tindakan untuk tampilan Detail menggunakan FirstOrDefaultAsync metode untuk mengambil satu Student entitas. Tambahkan kode yang memanggil Include. ThenInclude, dan AsNoTracking metode, seperti yang ditunjukkan dalam kode yang disorot berikut.

public async Task<IActionResult> Details(int? id)
{
    if (id == null)
    {
        return NotFound();
    }

    var student = await _context.Students
        .Include(s => s.Enrollments)
            .ThenInclude(e => e.Course)
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);

    if (student == null)
    {
        return NotFound();
    }

    return View(student);
}

Metode Include dan ThenInclude menyebabkan konteks memuat Student.Enrollments properti navigasi, dan dalam setiap pendaftaran Enrollment.Course properti navigasi. Anda akan mempelajari selengkapnya tentang metode ini dalam tutorial membaca data terkait.

Metode ini AsNoTracking meningkatkan performa dalam skenario di mana entitas yang dikembalikan tidak akan diperbarui dalam masa pakai konteks saat ini. Anda akan mempelajari lebih AsNoTracking lanjut di akhir tutorial ini.

Merutekan data

Nilai kunci yang diteruskan ke Details metode berasal dari data rute. Data rute adalah data yang ditemukan pengikat model di segmen URL. Misalnya, rute default menentukan segmen pengontrol, tindakan, dan id:

app.UseEndpoints(endpoints =>
{
    endpoints.MapControllerRoute(
        name: "default",
        pattern: "{controller=Home}/{action=Index}/{id?}");
});

Dalam URL berikut, rute default memetakan Instruktur sebagai pengontrol, Indeks sebagai tindakan, dan 1 sebagai id; ini adalah nilai data rute.

http://localhost:1230/Instructor/Index/1?courseID=2021

Bagian terakhir dari URL ("?courseID=2021") adalah nilai string kueri. Pengikat model juga akan meneruskan nilai ID ke Index parameter metode id jika Anda meneruskannya sebagai nilai string kueri:

http://localhost:1230/Instructor/Index?id=1&CourseID=2021

Di halaman Indeks, URL hyperlink dibuat oleh pernyataan pembantu Razor tag dalam tampilan. Dalam kode berikut Razor , id parameter cocok dengan rute default, sehingga id ditambahkan ke data rute.

<a asp-action="Edit" asp-route-id="@item.ID">Edit</a>

Ini menghasilkan HTML berikut ketika item.ID adalah 6:

<a href="/Students/Edit/6">Edit</a>

Dalam kode berikut Razor , studentID tidak cocok dengan parameter dalam rute default, sehingga ditambahkan sebagai string kueri.

<a asp-action="Edit" asp-route-studentID="@item.ID">Edit</a>

Ini menghasilkan HTML berikut ketika item.ID adalah 6:

<a href="/Students/Edit?studentID=6">Edit</a>

Untuk informasi selengkapnya tentang pembantu tag, lihat Pembantu Tag di ASP.NET Core.

Menambahkan pendaftaran ke tampilan Detail

Buka Views/Students/Details.cshtml. Setiap bidang ditampilkan menggunakan DisplayNameFor dan DisplayFor pembantu, seperti yang ditunjukkan dalam contoh berikut:

<dt class="col-sm-2">
    @Html.DisplayNameFor(model => model.LastName)
</dt>
<dd class="col-sm-10">
    @Html.DisplayFor(model => model.LastName)
</dd>

Setelah bidang terakhir dan segera sebelum tag penutup </dl> , tambahkan kode berikut untuk menampilkan daftar pendaftaran:

<dt class="col-sm-2">
    @Html.DisplayNameFor(model => model.Enrollments)
</dt>
<dd class="col-sm-10">
    <table class="table">
        <tr>
            <th>Course Title</th>
            <th>Grade</th>
        </tr>
        @foreach (var item in Model.Enrollments)
        {
            <tr>
                <td>
                    @Html.DisplayFor(modelItem => item.Course.Title)
                </td>
                <td>
                    @Html.DisplayFor(modelItem => item.Grade)
                </td>
            </tr>
        }
    </table>
</dd>

Jika indentasi kode salah setelah Anda menempelkan kode, tekan CTRL-K-D untuk memperbaikinya.

Kode ini mengulangi entitas di Enrollments properti navigasi. Untuk setiap pendaftaran, ini menampilkan judul kursus dan nilai. Judul kursus diambil dari entitas Kursus yang disimpan di Course properti navigasi entitas Pendaftaran.

Jalankan aplikasi, pilih tab Siswa , dan klik tautan Detail untuk siswa. Anda melihat daftar kursus dan nilai untuk siswa yang dipilih:

Student Details page

Memperbarui halaman Buat

Dalam StudentsController.cs, ubah metode HttpPost Create dengan menambahkan blok try-catch dan menghapus ID dari Bind atribut .

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
    [Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
{
    try
    {
        if (ModelState.IsValid)
        {
            _context.Add(student);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
    }
    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 View(student);
}

Kode ini menambahkan entitas Siswa yang dibuat oleh ASP.NET pengikat model Core MVC ke kumpulan entitas Siswa lalu menyimpan perubahan ke database. (Pengikat model mengacu pada fungsionalitas ASP.NET Core MVC yang memudahkan Anda untuk bekerja dengan data yang dikirimkan oleh formulir; pengikat model mengonversi nilai formulir yang diposting ke jenis CLR dan meneruskannya ke metode tindakan dalam parameter. Dalam hal ini, pengikat model membuat instans entitas Siswa untuk Anda menggunakan nilai properti dari koleksi Formulir.)

Anda menghapus ID dari Bind atribut karena ID adalah nilai kunci utama yang akan diatur SQL Server secara otomatis saat baris disisipkan. Input dari pengguna tidak mengatur nilai ID.

Bind Selain atribut , blok try-catch adalah satu-satunya perubahan yang Telah Anda lakukan pada kode perancah. Jika pengecualian yang berasal dari DbUpdateException tertangkap saat perubahan sedang disimpan, pesan kesalahan umum ditampilkan. DbUpdateException pengecualian terkadang disebabkan oleh sesuatu di luar aplikasi daripada kesalahan pemrograman, sehingga pengguna disarankan untuk mencoba lagi. Meskipun tidak diimplementasikan dalam sampel ini, aplikasi kualitas produksi akan mencatat pengecualian. Untuk informasi selengkapnya, lihat bagian Log untuk wawasan di Pemantauan dan Telemetri (Membangun Aplikasi Cloud Dunia Nyata dengan Azure).

Atribut ini ValidateAntiForgeryToken membantu mencegah serangan pemalsuan permintaan lintas situs (CSRF). Token secara otomatis dimasukkan ke dalam tampilan oleh FormTagHelper dan disertakan ketika formulir dikirimkan oleh pengguna. Token divalidasi oleh ValidateAntiForgeryToken atribut . Untuk informasi selengkapnya, lihat Mencegah serangan Pemalsuan Permintaan Antar Situs (XSRF/CSRF) di ASP.NET Core.

Catatan keamanan tentang overposting

Atribut Bind yang disertakan dalam kode perancah pada Create metode adalah salah satu cara untuk melindungi dari overposting dalam membuat skenario. Misalnya, entitas Siswa menyertakan Secret properti yang tidak ingin Anda atur halaman web ini.

public class Student
{
    public int ID { get; set; }
    public string LastName { get; set; }
    public string FirstMidName { get; set; }
    public DateTime EnrollmentDate { get; set; }
    public string Secret { get; set; }
}

Bahkan jika Anda tidak memiliki Secret bidang di halaman web, peretas dapat menggunakan alat seperti Fiddler, atau menulis beberapa JavaScript, untuk memposting Secret nilai formulir. Bind Tanpa atribut yang membatasi bidang yang digunakan pengikat model saat membuat instans Siswa, pengikat model akan mengambil Secret nilai formulir tersebut dan menggunakannya untuk membuat instans entitas Siswa. Kemudian nilai apa pun yang ditentukan peretas untuk Secret bidang formulir akan diperbarui dalam database Anda. Gambar berikut menunjukkan alat Fiddler yang menambahkan Secret bidang (dengan nilai "OverPost") ke nilai formulir yang diposting.

Fiddler adding Secret field

Nilai "OverPost" kemudian akan berhasil ditambahkan ke Secret properti baris yang disisipkan, meskipun Anda tidak pernah bermaksud agar halaman web dapat mengatur properti tersebut.

Anda dapat mencegah overposting dalam skenario edit dengan membaca entitas dari database terlebih dahulu lalu memanggil TryUpdateModel, meneruskan daftar properti eksplisit yang diizinkan. Itulah metode yang digunakan dalam tutorial ini.

Cara alternatif untuk mencegah overposting yang lebih disukai oleh banyak pengembang adalah dengan menggunakan model tampilan daripada kelas entitas dengan pengikatan model. Sertakan hanya properti yang ingin Anda perbarui dalam model tampilan. Setelah pengikat model MVC selesai, salin properti model tampilan ke instans entitas, secara opsional menggunakan alat seperti AutoMapper. Gunakan _context.Entry pada instans entitas untuk mengatur statusnya ke Unchanged, lalu atur Property("PropertyName").IsModified ke true pada setiap properti entitas yang disertakan dalam model tampilan. Metode ini berfungsi dalam skenario edit dan buat.

Menguji halaman Buat

Kode dalam Views/Students/Create.cshtml menggunakan pembantu labeltag , input, dan span (untuk pesan validasi) untuk setiap bidang.

Jalankan aplikasi, pilih tab Siswa , dan klik Buat Baru.

Masukkan nama dan tanggal. Coba masukkan tanggal yang tidak valid jika browser memungkinkan Anda melakukannya. (Beberapa browser memaksa Anda menggunakan pemilih tanggal.) Lalu klik Buat untuk melihat pesan kesalahan.

Date validation error

Ini adalah validasi sisi server yang Anda dapatkan secara default; dalam tutorial selanjutnya, Anda akan melihat cara menambahkan atribut yang akan menghasilkan kode untuk validasi sisi klien juga. Kode yang disorot berikut menunjukkan pemeriksaan validasi model dalam Create metode .

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(
    [Bind("EnrollmentDate,FirstMidName,LastName")] Student student)
{
    try
    {
        if (ModelState.IsValid)
        {
            _context.Add(student);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
    }
    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 View(student);
}

Ubah tanggal menjadi nilai yang valid dan klik Buat untuk melihat siswa baru muncul di halaman Indeks .

Memperbarui halaman Edit

Dalam StudentController.cs, metode HttpGet Edit (yang tanpa HttpPost atribut) menggunakan FirstOrDefaultAsync metode untuk mengambil entitas Siswa yang dipilih, seperti yang Anda lihat dalam Details metode . Anda tidak perlu mengubah metode ini.

Ganti metode tindakan HttpPost Edit dengan kode berikut.

[HttpPost, ActionName("Edit")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> EditPost(int? id)
{
    if (id == null)
    {
        return NotFound();
    }
    var studentToUpdate = await _context.Students.FirstOrDefaultAsync(s => s.ID == id);
    if (await TryUpdateModelAsync<Student>(
        studentToUpdate,
        "",
        s => s.FirstMidName, s => s.LastName, s => s.EnrollmentDate))
    {
        try
        {
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        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 View(studentToUpdate);
}

Perubahan ini menerapkan praktik terbaik keamanan untuk mencegah overposting. Perancah menghasilkan atribut dan menambahkan entitas yang Bind dibuat oleh pengikat model ke entitas yang Modified ditetapkan dengan bendera. Kode tersebut tidak disarankan untuk banyak skenario karena Bind atribut menghapus data yang sudah ada sebelumnya di bidang yang tidak tercantum dalam Include parameter.

Kode baru membaca entitas yang ada dan panggilan TryUpdateModel untuk memperbarui bidang di entitas yang diambil berdasarkan input pengguna dalam data formulir yang diposting. Pelacakan perubahan otomatis Entity Framework mengatur Modified bendera pada bidang yang diubah oleh input formulir. Ketika metode dipanggil SaveChanges , Kerangka Kerja Entitas membuat pernyataan SQL untuk memperbarui baris database. Konflik konkurensi diabaikan, dan hanya kolom tabel yang diperbarui oleh pengguna yang diperbarui dalam database. (Tutorial selanjutnya menunjukkan cara menangani konflik konkurensi.)

Sebagai praktik terbaik untuk mencegah overposting, bidang yang ingin Anda perbarui oleh halaman Edit dideklarasikan dalam TryUpdateModel parameter. (String kosong yang mendahului daftar bidang dalam daftar parameter adalah untuk awalan yang akan digunakan dengan nama bidang formulir.) Saat ini tidak ada bidang tambahan yang Anda lindungi, tetapi mencantumkan bidang yang Anda inginkan agar pengikat pengikat model memastikan bahwa jika Anda menambahkan bidang ke model data di masa mendatang, bidang tersebut secara otomatis dilindungi hingga Anda secara eksplisit menambahkannya di sini.

Akibat perubahan ini, tanda tangan metode metode HttpPost Edit sama dengan metode HttpGet Edit ; oleh karena itu Anda telah mengganti nama metode EditPost.

Kode Edit HttpPost alternatif: Membuat dan melampirkan

Kode edit HttpPost yang direkomendasikan memastikan bahwa hanya kolom yang diubah yang diperbarui dan mempertahankan data dalam properti yang tidak ingin Anda sertakan untuk pengikatan model. Namun, pendekatan baca-pertama memerlukan pembacaan database tambahan, dan dapat menghasilkan kode yang lebih kompleks untuk menangani konflik konkurensi. Alternatifnya adalah melampirkan entitas yang dibuat oleh pengikat model ke konteks EF dan menandainya sebagai dimodifikasi. (Jangan perbarui proyek Anda dengan kode ini, itu hanya ditampilkan untuk mengilustrasikan pendekatan opsional.)

public async Task<IActionResult> Edit(int id, [Bind("ID,EnrollmentDate,FirstMidName,LastName")] Student student)
{
    if (id != student.ID)
    {
        return NotFound();
    }
    if (ModelState.IsValid)
    {
        try
        {
            _context.Update(student);
            await _context.SaveChangesAsync();
            return RedirectToAction(nameof(Index));
        }
        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 View(student);
}

Anda dapat menggunakan pendekatan ini saat UI halaman web menyertakan semua bidang di entitas dan dapat memperbarui salah satunya.

Kode perancah menggunakan pendekatan create-and-attach tetapi hanya menangkap DbUpdateConcurrencyException pengecualian dan mengembalikan kode kesalahan 404. Contoh yang ditampilkan menangkap pengecualian pembaruan database apa pun dan menampilkan pesan kesalahan.

Status Entitas

Konteks database melacak apakah entitas dalam memori sinkron dengan baris yang sesuai dalam database, dan informasi ini menentukan apa yang terjadi saat Anda memanggil SaveChanges metode . Misalnya, ketika Anda meneruskan entitas baru ke Add metode , status entitas tersebut diatur ke Added. Kemudian saat Anda memanggil metode , SaveChanges konteks database mengeluarkan perintah SQL INSERT.

Entitas mungkin berada di salah satu status berikut:

  • Added. Entitas belum ada di database. Metode mengeluarkan SaveChanges pernyataan INSERT.

  • Unchanged. Tidak ada yang perlu dilakukan dengan entitas ini dengan SaveChanges metode . Saat Anda membaca entitas dari database, entitas dimulai dengan status ini.

  • Modified. Beberapa atau semua nilai properti entitas telah dimodifikasi. Metode ini SaveChanges mengeluarkan pernyataan UPDATE.

  • Deleted. Entitas telah ditandai untuk dihapus. Metode mengeluarkan SaveChanges pernyataan DELETE.

  • Detached. Entitas tidak dilacak oleh konteks database.

Dalam aplikasi desktop, perubahan status biasanya diatur secara otomatis. Anda membaca entitas dan membuat perubahan pada beberapa nilai propertinya. Ini menyebabkan status entitasnya secara otomatis diubah menjadi Modified. Kemudian saat Anda memanggil SaveChanges, Kerangka Kerja Entitas menghasilkan pernyataan PEMBARUAN SQL yang hanya memperbarui properti aktual yang Anda ubah.

Di aplikasi web, DbContext yang awalnya membaca entitas dan menampilkan datanya untuk diedit dibuang setelah halaman dirender. Ketika metode tindakan HttpPost Edit dipanggil, permintaan web baru dibuat dan Anda memiliki instans baru dari DbContext. Jika Anda membaca ulang entitas dalam konteks baru tersebut, Anda mensimulasikan pemrosesan desktop.

Tetapi jika Anda tidak ingin melakukan operasi baca tambahan, Anda harus menggunakan objek entitas yang dibuat oleh pengikat model. Cara paling sederhana untuk melakukan ini adalah dengan mengatur status entitas ke Dimodifikasi seperti yang dilakukan dalam kode HttpPost Edit alternatif yang ditunjukkan sebelumnya. Kemudian saat Anda memanggil SaveChanges, Kerangka Kerja Entitas memperbarui semua kolom baris database, karena konteks tidak memiliki cara untuk mengetahui properti mana yang Anda ubah.

Jika Anda ingin menghindari pendekatan baca-pertama, tetapi Anda juga ingin pernyataan PEMBARUAN SQL memperbarui hanya bidang yang benar-benar diubah pengguna, kodenya lebih kompleks. Anda harus menyimpan nilai asli dalam beberapa cara (seperti dengan menggunakan bidang tersembunyi) sehingga tersedia saat metode HttpPost Edit dipanggil. Kemudian Anda dapat membuat entitas Siswa menggunakan nilai asli, memanggil Attach metode dengan versi asli entitas tersebut, memperbarui nilai entitas ke nilai baru, lalu memanggil SaveChanges.

Menguji halaman Edit

Jalankan aplikasi, pilih tab Siswa , lalu klik Edit hyperlink.

Students edit page

Ubah beberapa data dan klik Simpan. Halaman Indeks terbuka dan Anda melihat data yang diubah.

Memperbarui halaman Hapus

Dalam StudentController.cs, kode templat untuk metode HttpGet Delete menggunakan FirstOrDefaultAsync metode untuk mengambil entitas Siswa yang dipilih, seperti yang Anda lihat dalam metode Detail dan Edit. Namun, untuk menerapkan pesan kesalahan kustom saat panggilan gagal SaveChanges , Anda akan menambahkan beberapa fungsionalitas ke metode ini dan tampilan yang sesuai.

Seperti yang Anda lihat untuk memperbarui dan membuat operasi, operasi penghapusan memerlukan dua metode tindakan. Metode yang dipanggil sebagai respons terhadap permintaan GET menampilkan tampilan yang memberi pengguna kesempatan untuk menyetujui atau membatalkan operasi penghapusan. Jika pengguna menyetujuinya, permintaan POST dibuat. Ketika itu terjadi, metode HttpPost Delete dipanggil dan kemudian metode itu benar-benar melakukan operasi penghapusan.

Anda akan menambahkan blok try-catch ke metode HttpPost Delete untuk menangani kesalahan apa pun yang mungkin terjadi saat database diperbarui. Jika terjadi kesalahan, metode Hapus HttpPost memanggil metode HttpGet Delete, meneruskannya parameter yang menunjukkan bahwa kesalahan telah terjadi. Metode Hapus HttpGet kemudian memutar ulang halaman konfirmasi bersama dengan pesan kesalahan, memberi pengguna kesempatan untuk membatalkan atau mencoba lagi.

Ganti metode tindakan HttpGet Delete dengan kode berikut, yang mengelola pelaporan kesalahan.

public async Task<IActionResult> Delete(int? id, bool? saveChangesError = false)
{
    if (id == null)
    {
        return NotFound();
    }

    var student = await _context.Students
        .AsNoTracking()
        .FirstOrDefaultAsync(m => m.ID == id);
    if (student == null)
    {
        return NotFound();
    }

    if (saveChangesError.GetValueOrDefault())
    {
        ViewData["ErrorMessage"] =
            "Delete failed. Try again, and if the problem persists " +
            "see your system administrator.";
    }

    return View(student);
}

Kode ini menerima parameter opsional yang menunjukkan apakah metode dipanggil setelah kegagalan untuk menyimpan perubahan. Parameter ini salah ketika metode HttpGet Delete dipanggil tanpa kegagalan sebelumnya. Ketika dipanggil oleh metode HttpPost Delete sebagai respons terhadap kesalahan pembaruan database, parameter tersebut benar dan pesan kesalahan diteruskan ke tampilan.

Pendekatan baca-pertama untuk Hapus HttpPost

Ganti metode tindakan HttpPost Delete (bernama DeleteConfirmed) dengan kode berikut, yang melakukan operasi penghapusan aktual dan menangkap kesalahan pembaruan database apa pun.

[HttpPost, ActionName("Delete")]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
    var student = await _context.Students.FindAsync(id);
    if (student == null)
    {
        return RedirectToAction(nameof(Index));
    }

    try
    {
        _context.Students.Remove(student);
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    catch (DbUpdateException /* ex */)
    {
        //Log the error (uncomment ex variable name and write a log.)
        return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
    }
}

Kode ini mengambil entitas yang dipilih, lalu memanggil Remove metode untuk mengatur status entitas ke Deleted. Ketika SaveChanges dipanggil, perintah SQL DELETE dihasilkan.

Pendekatan create-and-attach ke HttpPost Delete

Jika meningkatkan performa dalam aplikasi volume tinggi adalah prioritas, Anda dapat menghindari kueri SQL yang tidak perlu dengan membuat instans entitas Siswa hanya menggunakan nilai kunci utama lalu mengatur status entitas ke Deleted. Itu saja yang dibutuhkan Entity Framework untuk menghapus entitas. (Jangan letakkan kode ini dalam proyek Anda; kode ini ada di sini hanya untuk menggambarkan alternatif.)

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> DeleteConfirmed(int id)
{
    try
    {
        Student studentToDelete = new Student() { ID = id };
        _context.Entry(studentToDelete).State = EntityState.Deleted;
        await _context.SaveChangesAsync();
        return RedirectToAction(nameof(Index));
    }
    catch (DbUpdateException /* ex */)
    {
        //Log the error (uncomment ex variable name and write a log.)
        return RedirectToAction(nameof(Delete), new { id = id, saveChangesError = true });
    }
}

Jika entitas memiliki data terkait yang juga harus dihapus, pastikan bahwa penghapusan kaskade dikonfigurasi dalam database. Dengan pendekatan penghapusan entitas ini, EF mungkin tidak menyadari ada entitas terkait yang akan dihapus.

Memperbarui tampilan Hapus

Di Views/Student/Delete.cshtml, tambahkan pesan kesalahan antara judul h2 dan judul h3, seperti yang ditunjukkan dalam contoh berikut:

<h2>Delete</h2>
<p class="text-danger">@ViewData["ErrorMessage"]</p>
<h3>Are you sure you want to delete this?</h3>

Jalankan aplikasi, pilih tab Siswa , dan klik Hapus hyperlink:

Delete confirmation page

Klik Hapus. Halaman Indeks ditampilkan tanpa siswa yang dihapus. (Anda akan melihat contoh kode penanganan kesalahan dalam tindakan dalam tutorial konkurensi.)

Menutup koneksi database

Untuk membebaskan sumber daya yang disimpan koneksi database, instans konteks harus dibuang sesegera mungkin ketika Anda selesai dengannya. Injeksi dependensi bawaan ASP.NET Core mengurus tugas tersebut untuk Anda.

Dalam Startup.cs, Anda memanggil metode ekstensi AddDbContext untuk menyediakan DbContext kelas dalam kontainer ASP.NET Core DI. Metode tersebut mengatur masa pakai layanan menjadi Scoped secara default. Scoped berarti masa pakai objek konteks bertepatan dengan waktu hidup permintaan web, dan Dispose metode akan dipanggil secara otomatis di akhir permintaan web.

Menangani transaksi

Secara default, Entity Framework secara implisit mengimplementasikan transaksi. Dalam skenario di mana Anda membuat perubahan pada beberapa baris atau tabel lalu memanggil SaveChanges, Kerangka Kerja Entitas secara otomatis memastikan bahwa semua perubahan Anda berhasil atau semuanya gagal. Jika beberapa perubahan dilakukan terlebih dahulu dan kemudian terjadi kesalahan, perubahan tersebut secara otomatis digulung balik. 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.

Kueri tanpa pelacakan

Saat konteks database mengambil baris tabel dan membuat objek entitas yang mewakilinya, secara default konteks tersebut melacak apakah entitas dalam memori sinkron dengan apa yang ada di database. Data dalam memori bertindak sebagai cache dan digunakan saat Anda memperbarui entitas. Penembolokan ini sering kali tidak perlu dalam aplikasi web karena instans konteks biasanya berumur pendek (yang baru dibuat dan dibuang untuk setiap permintaan) dan konteks yang membaca entitas biasanya dibuang sebelum entitas tersebut digunakan lagi.

Anda dapat menonaktifkan pelacakan objek entitas dalam memori dengan memanggil AsNoTracking metode . Skenario umum di mana Anda mungkin ingin melakukannya termasuk yang berikut ini:

  • Selama masa pakai konteks, Anda tidak perlu memperbarui entitas apa pun, dan Anda tidak memerlukan EF untuk memuat properti navigasi secara otomatis dengan entitas yang diambil oleh kueri terpisah. Sering kali kondisi ini terpenuhi dalam metode tindakan HttpGet pengontrol.

  • Anda menjalankan kueri yang mengambil data dalam volume besar, dan hanya sebagian kecil data yang dikembalikan yang akan diperbarui. Mungkin lebih efisien untuk menonaktifkan pelacakan untuk kueri besar, dan menjalankan kueri nanti untuk beberapa entitas yang perlu diperbarui.

  • Anda ingin melampirkan entitas untuk memperbaruinya, tetapi sebelumnya Anda mengambil entitas yang sama untuk tujuan yang berbeda. Karena entitas sudah dilacak oleh konteks database, Anda tidak dapat melampirkan entitas yang ingin Anda ubah. Salah satu cara untuk menangani situasi ini adalah dengan memanggil AsNoTracking kueri sebelumnya.

Untuk informasi selengkapnya, lihat Pelacakan vs. Tanpa Pelacakan.

Mendapatkan kode

Unduh atau lihat aplikasi yang telah selesai.

Langkah berikutnya

Di tutorial ini, Anda akan:

  • Mengkustomisasi halaman Detail
  • Memperbarui halaman Buat
  • Memperbarui halaman Edit
  • Memperbarui halaman Hapus
  • Koneksi database tertutup

Lanjutkan ke tutorial berikutnya untuk mempelajari cara memperluas fungsionalitas halaman Indeks dengan menambahkan pengurutan, pemfilteran, dan penomoran halaman.