Bagikan melalui


Menangani Konkurensi dengan Kerangka Kerja Entitas dalam Aplikasi MVC ASP.NET (7 dari 10)

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 dua tutorial sebelumnya, Anda bekerja dengan data terkait. Tutorial ini menunjukkan cara menangani konkurensi. Anda akan membuat halaman web yang berfungsi dengan Department entitas, dan halaman yang mengedit dan menghapus Department entitas akan menangani kesalahan konkurensi. Ilustrasi berikut menunjukkan halaman Indeks dan Hapus, termasuk beberapa pesan yang ditampilkan jika terjadi konflik konkurensi.

Cuplikan layar memperlihatkan halaman Departemen Universitas Contoso sebelum pengeditan.

Cuplikan layar memperlihatkan halaman Universitas dengan pesan yang menjelaskan bahwa operasi dibatalkan karena nilainya telah diubah oleh pengguna lain.

Konflik Konkurensi

Konflik konkurensi terjadi ketika satu pengguna menampilkan data entitas untuk mengeditnya, lalu pengguna lain memperbarui data entitas yang sama sebelum perubahan pengguna pertama ditulis ke database. Jika Anda tidak mengaktifkan deteksi konflik tersebut, siapa pun yang memperbarui database terakhir kali menimpa perubahan pengguna lain. Dalam banyak aplikasi, risiko ini dapat diterima: jika ada beberapa pengguna, atau beberapa pembaruan, atau jika tidak terlalu penting jika beberapa perubahan ditimpa, biaya pemrograman untuk konkurensi mungkin melebihi manfaatnya. Dalam hal ini, Anda tidak perlu mengonfigurasi aplikasi untuk menangani konflik konkurensi.

Konkurensi Pesimis (Penguncian)

Jika aplikasi Anda memang perlu mencegah kehilangan data yang tidak disengaja dalam skenario konkurensi, salah satu cara untuk melakukannya adalah dengan menggunakan kunci database. Ini disebut konkurensi pesimis. Misalnya, sebelum Membaca baris dari database, Anda meminta kunci untuk akses baca-saja atau untuk pembaruan. Jika Anda mengunci baris untuk akses pembaruan, tidak ada pengguna lain yang diizinkan untuk mengunci baris baik untuk akses baca-saja atau perbarui, karena mereka akan mendapatkan salinan data yang sedang dalam proses diubah. Jika Anda mengunci baris untuk akses baca-saja, yang lain juga dapat menguncinya untuk akses baca-saja tetapi tidak untuk pembaruan.

Mengelola kunci memiliki kekurangan. Ini bisa menjadi kompleks untuk diprogram. Ini membutuhkan sumber daya manajemen database yang signifikan, dan dapat menyebabkan masalah performa saat jumlah pengguna aplikasi meningkat (artinya, itu tidak menskalakan dengan baik). Untuk alasan ini, tidak semua sistem manajemen database mendukung konkurensi pesimis. Kerangka Kerja Entitas tidak menyediakan dukungan bawaan untuk itu, dan tutorial ini tidak menunjukkan kepada Anda cara menerapkannya.

Konkurensi optimis

Alternatif untuk konkurensi pesimis adalah konkurensi optimis. Konkurensi optimis berarti memungkinkan konflik konkurensi terjadi, lalu bereaksi dengan tepat jika terjadi. Misalnya, John menjalankan halaman Edit Departemen, mengubah jumlah Anggaran untuk departemen Bahasa Inggris dari $350.000,00 menjadi $0,00.

Changing_English_dept_budget_to_100000

Sebelum John mengklik Simpan, Jane menjalankan halaman yang sama dan mengubah bidang Tanggal Mulai dari 1/9/2007 menjadi 8/8/2013.

Changing_English_dept_start_date_to_1999

John mengklik Simpan terlebih dahulu dan melihat perubahannya saat browser kembali ke halaman Indeks, lalu Jane mengklik Simpan. Apa yang terjadi selanjutnya ditentukan oleh cara Anda menangani konflik konkurensi. Beberapa opsi termasuk yang berikut ini:

  • Anda dapat melacak properti mana yang telah dimodifikasi pengguna dan memperbarui hanya kolom yang sesuai dalam database. Dalam skenario contoh, tidak ada data yang akan hilang, karena properti yang berbeda diperbarui oleh dua pengguna. Lain kali seseorang menelusuri departemen Inggris, mereka akan melihat perubahan John dan Jane — tanggal mulai 8/8/2013 dan anggaran nol dolar.

    Metode pembaruan ini dapat mengurangi jumlah konflik yang dapat mengakibatkan kehilangan data, tetapi tidak dapat menghindari kehilangan data jika perubahan yang bersaing dilakukan pada properti entitas yang sama. Apakah Kerangka Kerja Entitas berfungsi dengan cara ini tergantung pada cara Anda menerapkan kode pembaruan Anda. Seringkali tidak praktis dalam aplikasi web, karena dapat mengharuskan Anda mempertahankan status dalam jumlah besar untuk melacak semua nilai properti asli untuk entitas serta nilai baru. Mempertahankan status dalam jumlah besar dapat memengaruhi performa aplikasi karena memerlukan sumber daya server atau harus disertakan dalam halaman web itu sendiri (misalnya, di bidang tersembunyi).

  • Kau bisa membiarkan perubahan Jane menimpa perubahan John. Lain kali seseorang menelusuri departemen Bahasa Inggris, mereka akan melihat 8/8/2013 dan nilai $350,000.00 yang dipulihkan. Ini disebut skenario Client Wins atau Last in Wins . (Nilai klien lebih diutamakan daripada apa yang ada di penyimpanan data.) Seperti yang disebutkan dalam pengenalan bagian ini, jika Anda tidak melakukan pengkodan apa pun untuk penanganan konkurensi, ini akan terjadi secara otomatis.

  • Anda dapat mencegah perubahan Jane diperbarui dalam database. Biasanya, Anda akan menampilkan pesan kesalahan, menunjukkan status data saat ini, dan memungkinkannya untuk menerapkan kembali perubahannya jika dia masih ingin membuatnya. Ini disebut skenario Store Wins . (Nilai penyimpanan data lebih diutamakan daripada nilai yang dikirimkan oleh klien.) Anda akan menerapkan skenario Store Wins dalam tutorial ini. Metode ini memastikan bahwa tidak ada perubahan yang ditimpa tanpa pengguna diberitahu tentang apa yang terjadi.

Mendeteksi Konflik Konkurensi

Anda dapat mengatasi konflik dengan menangani pengecualian OptimisConcurrencyException yang dilemparkan Kerangka Kerja Entitas. Untuk mengetahui kapan harus melemparkan pengecualian ini, Kerangka Kerja Entitas harus dapat mendeteksi konflik. Oleh karena itu, Anda harus mengonfigurasi database dan model data dengan tepat. Beberapa opsi untuk mengaktifkan deteksi konflik meliputi yang berikut ini:

  • Dalam tabel database, sertakan kolom pelacakan yang dapat digunakan untuk menentukan kapan baris telah diubah. Anda kemudian dapat mengonfigurasi Kerangka Kerja Entitas untuk menyertakan kolom tersebut Where dalam klausul SQL Update atau Delete perintah.

    Jenis data kolom pelacakan biasanya rowversion. Nilai rowversion adalah angka berurutan yang bertahap setiap kali baris diperbarui. Dalam perintah Update atau Delete , Where klausul menyertakan nilai asli kolom pelacakan (versi baris asli). Jika baris yang diperbarui telah diubah oleh pengguna lain, nilai dalam rowversion kolom berbeda dari nilai asli, sehingga Update pernyataan atau Delete tidak dapat menemukan baris untuk diperbarui karena Where klausul. Ketika Kerangka Kerja Entitas menemukan bahwa tidak ada baris yang telah diperbarui oleh Update perintah atau Delete (yaitu, ketika jumlah baris yang terpengaruh adalah nol), itu menafsirkan bahwa sebagai konflik konkurensi.

  • Konfigurasikan Kerangka Kerja Entitas untuk menyertakan nilai asli setiap kolom dalam tabel dalam Where klausul Update dan Delete perintah.

    Seperti pada opsi pertama, jika apa pun dalam baris telah berubah sejak baris pertama kali dibaca, Where klausa tidak akan mengembalikan baris untuk diperbarui, yang ditafsirkan oleh Kerangka Kerja Entitas sebagai konflik konkurensi. Untuk tabel database yang memiliki banyak kolom, pendekatan ini dapat menghasilkan klausa yang sangat besar Where , dan dapat mengharuskan Anda mempertahankan status dalam jumlah besar. Seperti disebutkan sebelumnya, mempertahankan status dalam jumlah besar dapat memengaruhi performa aplikasi karena memerlukan sumber daya server atau harus disertakan dalam halaman web itu sendiri. Oleh karena itu pendekatan ini umumnya tidak direkomendasikan, dan bukan metode yang digunakan dalam tutorial ini.

    Jika Anda ingin menerapkan pendekatan ini ke konkurensi, Anda harus menandai semua properti kunci non-primer di entitas yang ingin Anda lacak konkurensinya dengan menambahkan atribut ConcurrencyCheck ke properti tersebut. Perubahan itu memungkinkan Kerangka Kerja Entitas untuk menyertakan semua kolom dalam klausa UPDATE pernyataan SQLWHERE.

Di sisa tutorial ini, Anda akan menambahkan properti pelacakan rowversion ke Department entitas, membuat pengontrol dan tampilan, dan menguji untuk memverifikasi bahwa semuanya berfungsi dengan benar.

Menambahkan Properti Konkurensi Optimis ke Entitas Departemen

Di Model\Department.cs, tambahkan properti pelacakan bernama RowVersion:

public class Department
{
    public int DepartmentID { get; set; }

    [StringLength(50, MinimumLength = 3)]
    public string Name { get; set; }

    [DataType(DataType.Currency)]
    [Column(TypeName = "money")]
    public decimal Budget { get; set; }

    [DataType(DataType.Date)]
    public DateTime StartDate { get; set; }

    [Display(Name = "Administrator")]
    public int? InstructorID { get; set; }

    [Timestamp]
    public byte[] RowVersion { get; set; }

    public virtual Instructor Administrator { get; set; }
    public virtual ICollection<Course> Courses { get; set; }
}

Atribut Tanda waktu menentukan bahwa kolom ini akan disertakan dalam Where klausul Update dan Delete perintah yang dikirim ke database. Atribut ini disebut Tanda Waktu karena versi SQL Server sebelumnya menggunakan jenis data tanda waktu SQL sebelum rowversion SQL menggantikannya. Jenis .Net untuk rowversion adalah array byte. Jika Anda lebih suka menggunakan API yang fasih, Anda dapat menggunakan metode IsConcurrencyToken untuk menentukan properti pelacakan, seperti yang ditunjukkan dalam contoh berikut:

modelBuilder.Entity<Department>()
    .Property(p => p.RowVersion).IsConcurrencyToken();

Lihat masalah GitHub Ganti IsConcurrencyToken oleh IsRowVersion.

Dengan menambahkan properti, Anda mengubah model database, jadi Anda perlu melakukan migrasi lain. Di Package Manager Console (PMC), masukkan perintah berikut:

Add-Migration RowVersion
Update-Database

Membuat Pengontrol Departemen

Buat Department pengontrol dan tampilan dengan cara yang sama seperti Anda melakukan pengontrol lain, menggunakan pengaturan berikut:

Add_Controller_dialog_box_for_Department_controller

Di Controllers\DepartmentController.cs, tambahkan using pernyataan:

using System.Data.Entity.Infrastructure;

Ubah "LastName" menjadi "FullName" di mana-mana dalam file ini (empat kemunculan) sehingga daftar drop-down administrator departemen akan berisi nama lengkap instruktur daripada hanya nama belakang.

ViewBag.InstructorID = new SelectList(db.Instructors, "InstructorID", "FullName");

Ganti kode yang HttpPost Edit ada untuk metode dengan kode berikut:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Edit(
   [Bind(Include = "DepartmentID, Name, Budget, StartDate, RowVersion, InstructorID")]
    Department department)
{
   try
   {
      if (ModelState.IsValid)
      {
         db.Entry(department).State = EntityState.Modified;
         db.SaveChanges();
         return RedirectToAction("Index");
      }
   }
   catch (DbUpdateConcurrencyException ex)
   {
      var entry = ex.Entries.Single();
      var clientValues = (Department)entry.Entity;
      var databaseValues = (Department)entry.GetDatabaseValues().ToObject();

      if (databaseValues.Name != clientValues.Name)
         ModelState.AddModelError("Name", "Current value: "
             + databaseValues.Name);
      if (databaseValues.Budget != clientValues.Budget)
         ModelState.AddModelError("Budget", "Current value: "
             + String.Format("{0:c}", databaseValues.Budget));
      if (databaseValues.StartDate != clientValues.StartDate)
         ModelState.AddModelError("StartDate", "Current value: "
             + String.Format("{0:d}", databaseValues.StartDate));
      if (databaseValues.InstructorID != clientValues.InstructorID)
         ModelState.AddModelError("InstructorID", "Current value: "
             + db.Instructors.Find(databaseValues.InstructorID).FullName);
      ModelState.AddModelError(string.Empty, "The record you attempted to edit "
          + "was modified by another user after you got the original value. The "
          + "edit operation was canceled and the current values in the database "
          + "have been displayed. If you still want to edit this record, click "
          + "the Save button again. Otherwise click the Back to List hyperlink.");
      department.RowVersion = databaseValues.RowVersion;
   }
   catch (DataException /* dex */)
   {
      //Log the error (uncomment dex variable name after DataException and add a line here to write a log.
      ModelState.AddModelError(string.Empty, "Unable to save changes. Try again, and if the problem persists contact your system administrator.");
   }

   ViewBag.InstructorID = new SelectList(db.Instructors, "InstructorID", "FullName", department.InstructorID);
   return View(department);
}

Tampilan akan menyimpan nilai asli RowVersion di bidang tersembunyi. Saat pengikat model membuat instans department , objek tersebut akan memiliki nilai properti asli RowVersion dan nilai baru untuk properti lain, seperti yang dimasukkan oleh pengguna di halaman Edit. Kemudian ketika Kerangka Kerja Entitas membuat perintah SQL UPDATE , perintah tersebut akan menyertakan WHERE klausa yang mencari baris yang memiliki nilai asli RowVersion .

Jika tidak ada baris yang dipengaruhi oleh UPDATE perintah (tidak ada baris yang memiliki nilai asli RowVersion ), Kerangka Kerja Entitas melemparkan DbUpdateConcurrencyException pengecualian, dan kode di catch blok mendapatkan entitas yang terpengaruh Department dari objek pengecualian. Entitas ini memiliki nilai yang dibaca dari database dan nilai baru yang dimasukkan oleh pengguna:

var entry = ex.Entries.Single();
var clientValues = (Department)entry.Entity;
var databaseValues = (Department)entry.GetDatabaseValues().ToObject();

Selanjutnya, kode menambahkan pesan kesalahan kustom untuk setiap kolom yang memiliki nilai database yang berbeda dari apa yang dimasukkan pengguna di halaman Edit:

if (databaseValues.Name != currentValues.Name)
    ModelState.AddModelError("Name", "Current value: " + databaseValues.Name);
    // ...

Pesan kesalahan yang lebih panjang menjelaskan apa yang terjadi dan apa yang harus dilakukan tentang hal itu:

ModelState.AddModelError(string.Empty, "The record you attempted to edit "
    + "was modified by another user after you got the original value. The"
    + "edit operation was canceled and the current values in the database "
    + "have been displayed. If you still want to edit this record, click "
    + "the Save button again. Otherwise click the Back to List hyperlink.");

Terakhir, kode menetapkan RowVersion nilai Department objek ke nilai baru yang diambil dari database. Nilai baru RowVersion ini akan disimpan di bidang tersembunyi ketika halaman Edit diputar ulang, dan saat berikutnya pengguna mengklik Simpan, hanya kesalahan konkurensi yang terjadi karena pemutaran ulang halaman Edit akan tertangkap.

Di Views\Department\Edit.cshtml, tambahkan bidang tersembunyi untuk menyimpan RowVersion nilai properti, segera mengikuti bidang tersembunyi untuk DepartmentID properti:

@model ContosoUniversity.Models.Department

@{
    ViewBag.Title = "Edit";
}

<h2>Edit</h2>

@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
    @Html.ValidationSummary(true)

    <fieldset>
        <legend>Department</legend>

        @Html.HiddenFor(model => model.DepartmentID)
        @Html.HiddenFor(model => model.RowVersion)

        <div class="editor-label">
            @Html.LabelFor(model => model.Name)
        </div>

Di Views\Department\Index.cshtml, ganti kode yang ada dengan kode berikut untuk memindahkan tautan baris ke kiri dan ubah judul halaman dan judul kolom untuk ditampilkan FullName alih-alih LastName di kolom Administrator :

@model IEnumerable<ContosoUniversity.Models.Department>

@{
    ViewBag.Title = "Departments";
}

<h2>Departments</h2>

<p>
    @Html.ActionLink("Create New", "Create")
</p>
<table>
    <tr>
        <th></th>
        <th>Name</th>
        <th>Budget</th>
        <th>Start Date</th>
        <th>Administrator</th>
    </tr>

@foreach (var item in Model) {
    <tr>
        <td>
            @Html.ActionLink("Edit", "Edit", new { id=item.DepartmentID }) |
            @Html.ActionLink("Details", "Details", new { id=item.DepartmentID }) |
            @Html.ActionLink("Delete", "Delete", new { id=item.DepartmentID })
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Name)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Budget)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.StartDate)
        </td>
        <td>
            @Html.DisplayFor(modelItem => item.Administrator.FullName)
        </td>
    </tr>
}

</table>

Pengujian Penanganan Konkurensi Optimis

Jalankan situs dan klik Departemen:

Cuplikan layar memperlihatkan halaman Departemen Universitas Contoso.

Klik kanan edit hyperlink untuk Kim Abercrombie dan pilih Buka di tab baru, lalu klik edit hyperlink untuk Kim Abercrombie. Dua jendela menampilkan informasi yang sama.

Department_Edit_page_before_changes

Ubah bidang di jendela browser pertama dan klik Simpan.

Department_Edit_page_1_after_change

Browser memperlihatkan halaman Indeks dengan nilai yang diubah.

Departments_Index_page_after_first_budget_edit

Ubah bidang apa pun di jendela browser kedua dan klik Simpan.

Department_Edit_page_2_after_change

Klik Simpan di jendela browser kedua. Anda melihat pesan kesalahan:

Cuplikan layar memperlihatkan halaman Universitas dengan pesan kesalahan, siap bagi pengguna untuk memilih Simpan lagi.

Klik Simpan lagi. Nilai yang Anda masukkan di browser kedua disimpan bersama dengan nilai asli data yang Anda ubah di browser pertama. Anda melihat nilai yang disimpan saat halaman Indeks muncul.

Department_Index_page_with_change_from_second_browser

Memperbarui Halaman Hapus

Untuk halaman Hapus, Kerangka Kerja Entitas mendeteksi konflik konkurensi yang disebabkan oleh orang lain yang mengedit departemen dengan cara yang sama. HttpGet Delete Saat metode menampilkan tampilan konfirmasi, tampilan menyertakan nilai asli RowVersion dalam bidang tersembunyi. Nilai tersebut kemudian tersedia untuk HttpPost Delete metode yang dipanggil ketika pengguna mengonfirmasi penghapusan. Saat Kerangka Kerja Entitas membuat perintah SQL DELETE , kerangka kerja menyertakan WHERE klausul dengan nilai asli RowVersion . Jika perintah menghasilkan nol baris yang terpengaruh (artinya baris diubah setelah halaman Konfirmasi penghapusan ditampilkan), pengecualian konkurensi dilemparkan, dan HttpGet Delete metode dipanggil dengan bendera kesalahan yang diatur ke true untuk memutar ulang halaman konfirmasi dengan pesan kesalahan. Ada kemungkinan juga bahwa nol baris terpengaruh karena baris dihapus oleh pengguna lain, jadi dalam hal ini pesan kesalahan yang berbeda ditampilkan.

Di DepartmentController.cs, ganti HttpGet Delete metode dengan kode berikut:

public ActionResult Delete(int id, bool? concurrencyError)
{
    Department department = db.Departments.Find(id);

    if (concurrencyError.GetValueOrDefault())
    {
        if (department == null)
        {
            ViewBag.ConcurrencyErrorMessage = "The record you attempted to delete "
                + "was deleted by another user after you got the original values. "
                + "Click the Back to List hyperlink.";
        }
        else
        {
            ViewBag.ConcurrencyErrorMessage = "The record you attempted to delete "
                + "was modified by another user after you got the original values. "
                + "The delete operation was canceled and the current values in the "
                + "database have been displayed. If you still want to delete this "
                + "record, click the Delete button again. Otherwise "
                + "click the Back to List hyperlink.";
        }
    }

    return View(department);
}

Metode ini menerima parameter opsional yang menunjukkan apakah halaman sedang diputar ulang setelah kesalahan konkurensi. Jika bendera ini adalah true, pesan kesalahan dikirim ke tampilan menggunakan ViewBag properti .

Ganti kode dalam HttpPost Delete metode (bernama DeleteConfirmed) dengan kode berikut:

[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Delete(Department department)
{
    try
    {
        db.Entry(department).State = EntityState.Deleted;
        db.SaveChanges();
        return RedirectToAction("Index");
    }
    catch (DbUpdateConcurrencyException)
    {
        return RedirectToAction("Delete", new { concurrencyError=true } );
    }
    catch (DataException /* dex */)
    {
        //Log the error (uncomment dex variable name after DataException and add a line here to write a log.
        ModelState.AddModelError(string.Empty, "Unable to delete. Try again, and if the problem persists contact your system administrator.");
        return View(department);
    }
}

Dalam kode perancah yang baru saja Anda ganti, metode ini hanya menerima ID rekaman:

public ActionResult DeleteConfirmed(int id)

Anda telah mengubah parameter ini menjadi instans entitas yang Department dibuat oleh pengikat model. Ini memberi Anda akses ke RowVersion nilai properti selain kunci rekaman.

public ActionResult Delete(Department department)

Anda juga telah mengubah nama metode tindakan dari DeleteConfirmed menjadi Delete. Kode perancah bernama HttpPost Delete metode DeleteConfirmed untuk memberikan HttpPost metode 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 untuk HttpPost metode dan HttpGet hapus.

Jika kesalahan konkurensi tertangkap, kode akan menampilkan kembali halaman Konfirmasi penghapusan dan menyediakan bendera yang menunjukkan bahwa kode harus menampilkan pesan kesalahan konkurensi.

Di Views\Department\Delete.cshtml, ganti kode perancah dengan kode berikut yang membuat beberapa perubahan pemformatan dan menambahkan bidang pesan kesalahan. Perubahan disorot.

@model ContosoUniversity.Models.Department

@{
    ViewBag.Title = "Delete";
}

<h2>Delete</h2>

<p class="error">@ViewBag.ConcurrencyErrorMessage</p>

<h3>Are you sure you want to delete this?</h3>
<fieldset>
    <legend>Department</legend>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Name)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Name)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Budget)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Budget)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.StartDate)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.StartDate)
    </div>

    <div class="display-label">
         @Html.DisplayNameFor(model => model.Administrator.FullName)
    </div>
    <div class="display-field">
        @Html.DisplayFor(model => model.Administrator.FullName)
    </div>
</fieldset>
@using (Html.BeginForm()) {
    @Html.AntiForgeryToken()
   @Html.HiddenFor(model => model.DepartmentID)
    @Html.HiddenFor(model => model.RowVersion)
    <p>
        <input type="submit" value="Delete" /> |
        @Html.ActionLink("Back to List", "Index")
    </p>
}

Kode ini menambahkan pesan kesalahan antara judul h2 dan h3 :

<p class="error">@ViewBag.ConcurrencyErrorMessage</p>

Ini menggantikan LastName dengan FullName di Administrator bidang :

<div class="display-label">
    @Html.LabelFor(model => model.InstructorID)
</div>
<div class="display-field">
    @Html.DisplayFor(model => model.Administrator.FullName)
</div>

Terakhir, ia menambahkan bidang tersembunyi untuk DepartmentID properti dan RowVersion setelah Html.BeginForm pernyataan:

@Html.HiddenFor(model => model.DepartmentID)
@Html.HiddenFor(model => model.RowVersion)

Jalankan halaman Indeks Departemen. Klik kanan hyperlink Hapus untuk departemen bahasa Inggris dan pilih Buka di jendela baru, lalu di jendela pertama klik hyperlink Edit untuk departemen bahasa Inggris.

Di jendela pertama, ubah salah satu nilai, dan klik Simpan :

Department_Edit_page_after_change_before_delete

Halaman Indeks mengonfirmasi perubahan.

Departments_Index_page_after_budget_edit_before_delete

Di jendela kedua, klik Hapus.

Department_Delete_confirmation_page_before_concurrency_error

Anda melihat pesan kesalahan konkurensi, dan nilai Departemen di-refresh dengan apa yang saat ini ada di database.

Department_Delete_confirmation_page_with_concurrency_error

Jika Anda mengklik Hapus lagi, Anda dialihkan ke halaman Indeks, yang menunjukkan bahwa departemen telah dihapus.

Ringkasan

Ini menyelesaikan pengenalan penanganan konflik konkurensi. Untuk informasi tentang cara lain untuk menangani berbagai skenario konkurensi, lihat Pola Konkurensi Optimis dan Bekerja dengan Nilai Properti di blog tim Kerangka Kerja Entitas. Tutorial berikutnya menunjukkan cara menerapkan warisan tabel per hierarki untuk Instructor entitas dan Student .

Tautan ke sumber daya Entity Framework lainnya dapat ditemukan di Peta Konten Akses Data ASP.NET.