Catatan
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba masuk atau mengubah direktori.
Akses ke halaman ini memerlukan otorisasi. Anda dapat mencoba mengubah direktori.
oleh Tom Dykstra
Aplikasi web sampel Contoso University menunjukkan cara membuat aplikasi ASP.NET MVC 4 menggunakan Entity Framework 5 Code First dan Visual Studio 2012. Untuk informasi tentang seri tutorial, lihat tutorial pertama dalam seri ini.
Catatan
Jika Anda mengalami masalah yang tidak dapat Anda atasi, unduh bab yang telah selesai dan coba reprodurasi masalah Anda. Anda umumnya dapat menemukan solusi untuk masalah dengan membandingkan kode Anda dengan kode yang telah selesai. Untuk beberapa kesalahan umum dan cara mengatasinya, lihat Kesalahan dan Solusi.
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. Agar tutorial ini tetap sederhana, Anda tidak akan menerapkan repositori hingga tutorial selanjutnya dalam seri ini.
Dalam tutorial ini, Anda akan membuat halaman web berikut:




Membuat Halaman Detail
Kode perancah untuk halaman Siswa Index meninggalkan Enrollments properti, karena properti tersebut menyimpan koleksi. Details Di halaman Anda akan menampilkan konten koleksi dalam tabel HTML.
Di Controllers\StudentController.cs, metode tindakan untuk Details tampilan menggunakan Find metode untuk mengambil satu Student entitas.
public ActionResult Details(int id = 0)
{
Student student = db.Students.Find(id);
if (student == null)
{
return HttpNotFound();
}
return View(student);
}
Nilai kunci diteruskan ke metode sebagai id parameter dan berasal dari data rute dalam hyperlink Detail di halaman Indeks.
Buka Views\Student\Details.cshtml. Setiap bidang ditampilkan menggunakan pembantu, seperti yang
DisplayFordiperlihatkan dalam contoh berikut:<div class="display-label"> @Html.DisplayNameFor(model => model.LastName) </div> <div class="display-field"> @Html.DisplayFor(model => model.LastName) </div>EnrollmentDateSetelah bidang dan segera sebelum tag penutupfieldset, tambahkan kode untuk menampilkan daftar pendaftaran, seperti yang ditunjukkan dalam contoh berikut:<div class="display-label"> @Html.LabelFor(model => model.Enrollments) </div> <div class="display-field"> <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> </div> </fieldset> <p> @Html.ActionLink("Edit", "Edit", new { id=Model.StudentID }) | @Html.ActionLink("Back to List", "Index") </p>Kode ini mengulangi entitas di
Enrollmentsproperti navigasi. Untuk setiapEnrollmententitas dalam properti, entitas menampilkan judul kursus dan nilainya. Judul kursus diambil dariCourseentitas yang disimpan diCoursepropertiEnrollmentsnavigasi entitas. Semua data ini diambil dari database secara otomatis saat diperlukan. (Dengan kata lain, Anda menggunakan pemuatan malas di sini. Anda tidak menentukan pemuatan bersemangat untukCoursesproperti navigasi, jadi pertama kali Anda mencoba mengakses properti tersebut, kueri dikirim ke database untuk mengambil data. Anda dapat membaca lebih lanjut tentang pemuatan malas dan pemuatan yang bersemangat dalam tutorial Membaca Data Terkait nanti dalam seri ini.)Jalankan halaman dengan memilih tab Siswa dan mengklik tautan Detail untuk Alexander Carson. Anda melihat daftar kursus dan nilai untuk siswa yang dipilih:

Memperbarui Halaman Buat
Di Controllers\StudentController.cs, ganti
HttpPost``Createmetode tindakan dengan kode berikut untuk menambahkantry-catchblok dan atribut Bind ke metode scaffolded:[HttpPost] [ValidateAntiForgeryToken] public ActionResult Create( [Bind(Include = "LastName, FirstMidName, EnrollmentDate")] Student student) { try { if (ModelState.IsValid) { db.Students.Add(student); db.SaveChanges(); return RedirectToAction("Index"); } } catch (DataException /* dex */) { //Log the error (uncomment dex variable name after DataException and add a line here to 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 yang
Studentdibuat oleh pengikat model MVC ASP.NET keStudentskumpulan entitas lalu menyimpan perubahan ke database. (Pengikat model mengacu pada fungsionalitas MVC ASP.NET 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 instansStudententitas untuk Anda menggunakan nilai properti dariFormkoleksi.)Atribut ini
ValidateAntiForgeryTokenmembantu mencegah serangan pemalsuan permintaan lintas situs.
> [!WARNING]
> Security - The `Bind` attribute is added to protect against *over-posting*. For example, suppose the `Student` entity includes a `Secret` property that you don't want this web page to update.
>
> [!code-csharp[Main](implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application/samples/sample5.cs?highlight=7)]
>
> Even if you don't have a `Secret` field on the web page, a hacker could use a tool such as [fiddler](http://fiddler2.com/home), or write some JavaScript, to post a `Secret` form value. Without the [Bind](https://msdn.microsoft.com/library/system.web.mvc.bindattribute(v=vs.108).aspx) attribute limiting the fields that the model binder uses when it creates a `Student` instance*,* the model binder would pick up that `Secret` form value and use it to update the `Student` entity instance. Then whatever value the hacker specified for the `Secret` form field would be updated in your database. The following image shows the fiddler tool adding the `Secret` field (with the value "OverPost") to the posted form values.
>
> 
>
> The value "OverPost" would then be successfully added to the `Secret` property of the inserted row, although you never intended that the web page be able to update that property.
>
> It's a security best practice to use the `Include` parameter with the `Bind` attribute to *allowed attributes* fields. It's also possible to use the `Exclude` parameter to *blocked attributes* fields you want to exclude. The reason `Include` is more secure is that when you add a new property to the entity, the new field is not automatically protected by an `Exclude` list.
>
> Another alternative approach, and one preferred by many, is to use only view models with model binding. The view model contains only the properties you want to bind. Once the MVC model binder has finished, you copy the view model properties to the entity instance.
Other than the `Bind` attribute, the `try-catch` block is the only change you've made to the scaffolded code. If an exception that derives from [DataException](https://msdn.microsoft.com/library/system.data.dataexception.aspx) is caught while the changes are being saved, a generic error message is displayed. [DataException](https://msdn.microsoft.com/library/system.data.dataexception.aspx) exceptions are sometimes caused by something external to the application rather than a programming error, so the user is advised to try again. Although not implemented in this sample, a production quality application would log the exception (and non-null inner exceptions ) with a logging mechanism such as [ELMAH](https://code.google.com/p/elmah/).
The code in *Views\Student\Create.cshtml* is similar to what you saw in *Details.cshtml*, except that `EditorFor` and `ValidationMessageFor` helpers are used for each field instead of `DisplayFor`. The following example shows the relevant code:
[!code-cshtml[Main](implementing-basic-crud-functionality-with-the-entity-framework-in-asp-net-mvc-application/samples/sample6.cshtml)]
*Create.cshtml* also includes `@Html.AntiForgeryToken()`, which works with the `ValidateAntiForgeryToken` attribute in the controller to help prevent [cross-site request forgery](../../security/xsrfcsrf-prevention-in-aspnet-mvc-and-web-pages.md) attacks.
No changes are required in *Create.cshtml*.
Jalankan halaman dengan memilih tab Siswa dan klik Buat Baru.

Beberapa validasi data berfungsi secara default. Masukkan nama dan tanggal yang tidak valid dan klik Buat untuk melihat pesan kesalahan.

Kode yang disorot berikut menunjukkan pemeriksaan validasi model.
[HttpPost] [ValidateAntiForgeryToken] public ActionResult Create(Student student) { if (ModelState.IsValid) { db.Students.Add(student); db.SaveChanges(); return RedirectToAction("Index"); } return View(student); }Ubah tanggal menjadi nilai yang valid seperti 1/9/2005 dan klik Buat untuk melihat siswa baru muncul di halaman Indeks .

Memperbarui Halaman Edit POST
Di Controllers\StudentController.cs, HttpGet Edit metode (yang tanpa HttpPost atribut) menggunakan Find metode untuk mengambil entitas yang dipilih, seperti yang Student Anda lihat dalam Details metode . Anda tidak perlu mengubah metode ini.
Namun, ganti HttpPost Edit metode tindakan dengan kode berikut untuk menambahkan try-catch blok dan atribut Bind:
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
[Bind(Include = "StudentID, LastName, FirstMidName, EnrollmentDate")]
Student student)
{
try
{
if (ModelState.IsValid)
{
db.Entry(student).State = EntityState.Modified;
db.SaveChanges();
return RedirectToAction("Index");
}
}
catch (DataException /* dex */)
{
//Log the error (uncomment dex variable name after DataException and add a line here to 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 mirip dengan apa yang Anda lihat dalam HttpPost Create metode . Namun, alih-alih menambahkan entitas yang dibuat oleh pengikat model ke kumpulan entitas, kode ini menetapkan bendera pada entitas yang menunjukkan bahwa entitas telah diubah. Ketika metode SaveChanges dipanggil, bendera Diubah menyebabkan Kerangka Kerja Entitas membuat pernyataan SQL untuk memperbarui baris database. Semua kolom baris database akan diperbarui, termasuk kolom yang tidak diubah pengguna, dan konflik konkurensi diabaikan. (Anda akan mempelajari cara menangani konkurensi dalam tutorial selanjutnya dalam seri ini.)
Status Entitas dan Metode Lampirkan dan SaveChanges
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, saat Anda meneruskan entitas baru ke metode Tambahkan , 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. MetodeSaveChangesharus mengeluarkanINSERTpernyataan.Unchanged. Tidak ada yang perlu dilakukan dengan entitas ini denganSaveChangesmetode . Saat Anda membaca entitas dari database, entitas dimulai dengan status ini.Modified. Beberapa atau semua nilai properti entitas telah dimodifikasi. MetodeSaveChangesharus mengeluarkanUPDATEpernyataan.Deleted. Entitas telah ditandai untuk dihapus. MetodeSaveChangesharus mengeluarkanDELETEpernyataan.Detached. Entitas tidak dilacak oleh konteks database.
Dalam aplikasi desktop, perubahan status biasanya diatur secara otomatis. Dalam jenis aplikasi desktop, 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 SQL UPDATE yang hanya memperbarui properti aktual yang Anda ubah.
Sifat aplikasi web yang terputus tidak memungkinkan urutan berkelanjutan ini. DbContext yang membaca entitas dibuang setelah halaman dirender. HttpPost Edit Ketika metode tindakan dipanggil, permintaan baru dibuat dan Anda memiliki instans baru DbContext, jadi Anda harus mengatur status entitas secara manual ke Modified. Kemudian ketika 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 pernyataan SQL Update hanya memperbarui bidang yang benar-benar diubah pengguna, Anda dapat menyimpan nilai asli dalam beberapa cara (seperti bidang tersembunyi) sehingga tersedia saat HttpPost Edit metode dipanggil. Kemudian Anda dapat membuat Student entitas menggunakan nilai asli, memanggil Attach metode dengan versi asli entitas tersebut, memperbarui nilai entitas ke nilai baru, lalu memanggil SaveChanges. Untuk informasi selengkapnya, lihat Status entitas dan SaveChanges dan Data Lokal di Pusat Pengembang Data MSDN.
Kode dalam Views\Student\Edit.cshtml mirip dengan apa yang Anda lihat di Create.cshtml, dan tidak ada perubahan yang diperlukan.
Jalankan halaman dengan memilih tab Siswa lalu klik Edit hyperlink.

Ubah beberapa data dan klik Simpan. Anda melihat data yang diubah di halaman Indeks.

Memperbarui Halaman Hapus
Di Controllers\StudentController.cs, kode templat untuk HttpGet Delete metode menggunakan Find metode untuk mengambil entitas yang dipilih, seperti yang Student Anda lihat dalam Details metode 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 dipanggil HttpPost Delete dan kemudian metode itu benar-benar melakukan operasi penghapusan.
Anda akan menambahkan try-catch blok ke HttpPost Delete metode untuk menangani kesalahan apa pun yang mungkin terjadi saat database diperbarui. Jika terjadi kesalahan, HttpPost Delete metode memanggil HttpGet Delete metode , meneruskannya parameter yang menunjukkan bahwa kesalahan telah terjadi. Metode kemudian HttpGet Delete memutar ulang halaman konfirmasi bersama dengan pesan kesalahan, memberi pengguna kesempatan untuk membatalkan atau mencoba lagi.
HttpGetDeleteGanti metode tindakan dengan kode berikut, yang mengelola pelaporan kesalahan:public ActionResult Delete(bool? saveChangesError=false, int id = 0) { if (saveChangesError.GetValueOrDefault()) { ViewBag.ErrorMessage = "Delete failed. Try again, and if the problem persists see your system administrator."; } Student student = db.Students.Find(id); if (student == null) { return HttpNotFound(); } return View(student); }Kode ini menerima parameter Boolean opsional yang menunjukkan apakah itu dipanggil setelah kegagalan untuk menyimpan perubahan. Parameter ini adalah
falseketika metode dipanggilHttpGetDeletetanpa kegagalan sebelumnya. Ketika dipanggil olehHttpPostDeletemetode sebagai respons terhadap kesalahan pembaruan database, parameternya adalahtruedan pesan kesalahan diteruskan ke tampilan.HttpPostDeleteGanti metode tindakan (bernamaDeleteConfirmed) dengan kode berikut, yang melakukan operasi penghapusan aktual dan menangkap kesalahan pembaruan database apa pun.[HttpPost] [ValidateAntiForgeryToken] public ActionResult Delete(int id) { try { Student student = db.Students.Find(id); db.Students.Remove(student); db.SaveChanges(); } catch (DataException/* dex */) { // uncomment dex and log error. return RedirectToAction("Delete", new { id = id, saveChangesError = true }); } return RedirectToAction("Index"); }Kode ini mengambil entitas yang dipilih, lalu memanggil metode Hapus untuk mengatur status entitas ke
Deleted. KetikaSaveChangesdipanggil, perintah SQLDELETEdihasilkan. Anda juga telah mengubah nama metode tindakan dariDeleteConfirmedmenjadiDelete. Kode perancah bernamaHttpPostDeletemetodeDeleteConfirmeduntuk memberikanHttpPostmetode tanda tangan unik. ( CLR memerlukan metode kelebihan beban untuk memiliki parameter metode yang berbeda.) Sekarang setelah tanda tangan unik, Anda dapat tetap dengan konvensi MVC dan menggunakan nama yang sama untukHttpPostmetode danHttpGethapus.Jika meningkatkan performa dalam aplikasi volume tinggi adalah prioritas, Anda dapat menghindari kueri SQL yang tidak perlu untuk mengambil baris dengan mengganti baris kode yang memanggil
Findmetode danRemovedengan kode berikut seperti yang ditunjukkan dalam sorotan kuning:Student studentToDelete = new Student() { StudentID = id }; db.Entry(studentToDelete).State = EntityState.Deleted;Kode ini membuat
Studentinstans entitas hanya menggunakan nilai kunci utama lalu mengatur status entitas keDeleted. Itu saja yang dibutuhkan Entity Framework untuk menghapus entitas.Seperti yang disebutkan,
HttpGetDeletemetode tidak menghapus data. Melakukan operasi penghapusan sebagai respons terhadap permintaan GET (atau untuk hal itu, melakukan operasi edit apa pun, membuat operasi, atau operasi lain yang mengubah data) membuat risiko keamanan. Untuk informasi selengkapnya, lihat ASP.NET Tip MVC #46 — Jangan gunakan Hapus Tautan karena mereka membuat Lubang Keamanan di blog Stephen Walther.Di Views\Student\Delete.cshtml, tambahkan pesan kesalahan antara
h2judul danh3judul, seperti yang ditunjukkan dalam contoh berikut:<h2>Delete</h2> <p class="error">@ViewBag.ErrorMessage</p> <h3>Are you sure you want to delete this?</h3>Jalankan halaman dengan memilih tab Siswa dan mengklik Hapus hyperlink:

Klik Hapus. Halaman Indeks ditampilkan tanpa siswa yang dihapus. (Anda akan melihat contoh kode penanganan kesalahan dalam tindakan di Menangani tutorial Konkurensi nanti dalam seri ini.)
Memastikan Bahwa Koneksi Database Tidak Dibiarkan Terbuka
Untuk memastikan bahwa koneksi database ditutup dengan benar dan sumber daya yang dibeberkan, Anda akan melihatnya bahwa instans konteks dibuang. Itulah sebabnya kode perancah menyediakan metode Buang di akhir StudentController kelas di StudentController.cs, seperti yang ditunjukkan dalam contoh berikut:
protected override void Dispose(bool disposing)
{
db.Dispose();
base.Dispose(disposing);
}
Kelas dasar Controller sudah mengimplementasikan IDisposable antarmuka, sehingga kode ini hanya menambahkan penimpaan ke Dispose(bool) metode untuk secara eksplisit membuang instans konteks.
Ringkasan
Anda sekarang memiliki sekumpulan halaman lengkap yang melakukan operasi CRUD sederhana untuk Student entitas. Anda menggunakan pembantu MVC untuk menghasilkan elemen UI untuk bidang data. Untuk informasi selengkapnya tentang pembantu MVC, lihat Merender Formulir Menggunakan Pembantu HTML (halaman tersebut untuk MVC 3 tetapi masih relevan untuk MVC 4).
Dalam tutorial berikutnya, Anda akan memperluas fungsionalitas halaman Indeks dengan menambahkan pengurutan dan penomoran halaman.
Tautan ke sumber daya Entity Framework lainnya dapat ditemukan di Peta Konten Akses Data ASP.NET.