Bagikan melalui


Menangani Konkurensi dengan Kerangka Kerja Entitas 4.0 dalam Aplikasi Web ASP.NET 4

oleh Tom Dykstra

Seri tutorial ini dibangun pada aplikasi web Contoso University yang dibuat oleh seri tutorial Memulai Entity Framework 4.0 . Jika Anda tidak menyelesaikan tutorial sebelumnya, sebagai titik awal untuk tutorial ini, Anda dapat mengunduh aplikasi yang akan Anda buat. Anda juga dapat mengunduh aplikasi yang dibuat oleh seri tutorial lengkap. Jika Anda memiliki pertanyaan tentang tutorial, Anda dapat mempostingnya ke forum ASP.NET Entity Framework.

Dalam tutorial sebelumnya Anda mempelajari cara mengurutkan dan memfilter data menggunakan ObjectDataSource kontrol dan Kerangka Kerja Entitas. Tutorial ini menunjukkan opsi untuk menangani konkurensi dalam aplikasi web ASP.NET yang menggunakan Kerangka Kerja Entitas. Anda akan membuat halaman web baru yang didedikasikan untuk memperbarui penugasan kantor instruktur. Anda akan menangani masalah konkurensi di halaman tersebut dan di halaman Departemen yang Anda buat sebelumnya.

Gambar06

Gambar01

Konflik Konkurensi

Konflik konkurensi terjadi ketika satu pengguna mengedit rekaman dan pengguna lain mengedit rekaman yang sama sebelum perubahan pengguna pertama ditulis ke database. Jika Anda tidak menyiapkan Kerangka Kerja Entitas untuk mendeteksi konflik tersebut, siapa pun yang memperbarui database terakhir kali menimpa perubahan pengguna lain. Dalam banyak aplikasi, risiko ini dapat diterima, dan Anda tidak perlu mengonfigurasi aplikasi untuk menangani kemungkinan konflik konkurensi. (Jika ada beberapa pengguna, atau beberapa pembaruan, atau jika tidak terlalu penting jika beberapa perubahan ditimpa, biaya pemrograman untuk konkurensi mungkin melebihi manfaatnya.) Jika Anda tidak perlu khawatir tentang konflik konkurensi, Anda dapat melewati tutorial ini; dua tutorial yang tersisa dalam seri tidak bergantung pada apa pun yang Anda bangun dalam yang satu ini.

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, orang lain juga dapat menguncinya untuk akses baca-saja tetapi tidak untuk pembaruan.

Mengelola kunci memiliki beberapa kelemahan. Ini bisa rumit 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 mengimplementasikannya.

Konkurensi optimis

Alternatif untuk konkurensi pesimis adalah konkurensi optimis. Konkurensi optimis berarti memungkinkan konflik konkurensi terjadi, dan kemudian bereaksi dengan tepat jika terjadi. Misalnya, John menjalankan halaman Department.aspx , mengklik tautan Edit untuk departemen Riwayat, dan mengurangi jumlah Anggaran dari $1.000.000,00 menjadi $125.000,00. (John mengelola departemen yang bersaing dan ingin membebaskan uang untuk departemennya sendiri.)

Gambar07

Sebelum John mengklik Perbarui, Jane menjalankan halaman yang sama, mengklik tautan Edit untuk departemen Riwayat, lalu mengubah bidang Tanggal Mulai dari 10/10/2011 menjadi 1/1/1999. (Jane mengelola departemen Sejarah dan ingin memberikannya lebih senioritas.)

Gambar08

John mengklik Perbarui terlebih dahulu, lalu Jane mengklik Perbarui. Browser Jane sekarang mencantumkan jumlah Anggaran sebesar $1.000.000,00, tetapi ini salah karena jumlahnya telah diubah oleh John menjadi $125.000,00.

Beberapa tindakan yang dapat Anda lakukan dalam skenario ini meliputi yang berikut ini:

  • Anda dapat melacak properti mana yang telah dimodifikasi pengguna dan hanya memperbarui kolom terkait dalam database. Dalam skenario contoh, tidak ada data yang akan hilang, karena properti yang berbeda diperbarui oleh dua pengguna. Lain kali seseorang menelusuri departemen Sejarah, mereka akan melihat 1/1/1999 dan $ 125.000,00.

    Ini adalah perilaku default dalam Kerangka Kerja Entitas, dan dapat secara substansial mengurangi jumlah konflik yang dapat mengakibatkan kehilangan data. Namun, perilaku ini tidak menghindari kehilangan data jika perubahan yang bersaing dilakukan pada properti entitas yang sama. Selain itu, perilaku ini tidak selalu dimungkinkan; saat Anda memetakan prosedur tersimpan ke jenis entitas, semua properti entitas diperbarui ketika ada perubahan pada entitas yang dibuat dalam database.

  • Kau bisa membiarkan perubahan Jane menimpa perubahan John. Setelah Jane mengklik Perbarui, jumlah Anggaran kembali ke $1.000.000,00. Ini disebut skenario Client Wins atau Last in Wins . (Nilai klien lebih diutamakan daripada apa yang ada di penyimpanan data.)

  • Anda dapat mencegah perubahan Jane diperbarui dalam database. Biasanya, Anda akan menampilkan pesan kesalahan, menunjukkan status data saat ini, dan memungkinkannya untuk masuk kembali perubahannya jika dia masih ingin membuatnya. Anda dapat mengotomatiskan proses lebih lanjut dengan menyimpan masukannya dan memberinya kesempatan untuk menerapkannya kembali tanpa harus memasukkannya kembali. Ini disebut skenario Store Wins . (Nilai penyimpanan data lebih diutamakan daripada nilai yang dikirimkan oleh klien.)

Mendeteksi Konflik Konkurensi

Dalam Kerangka Kerja Entitas, Anda dapat mengatasi konflik dengan menangani OptimisticConcurrencyException pengecualian 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 database, sertakan kolom tabel yang bisa digunakan untuk menentukan kapan baris telah diubah. Anda kemudian dapat mengonfigurasi Kerangka Kerja Entitas untuk menyertakan kolom tersebut Where dalam klausa SQL Update atau Delete perintah.

    Itulah tujuan Timestamp kolom dalam OfficeAssignment tabel.

    Gambar09

    Jenis Timestamp data kolom juga disebut Timestamp. Namun, kolom sebenarnya tidak berisi nilai tanggal atau waktu. Sebaliknya, nilainya adalah angka berurutan yang bertahap setiap kali baris diperbarui. Update Dalam perintah atau Delete , Where klausul menyertakan nilai asliTimestamp. Jika baris yang diperbarui telah diubah oleh pengguna lain, nilai di berbeda dari nilai asli, sehingga Where klausa tidak mengembalikan baris untuk diperbaruiTimestamp. Ketika Kerangka Kerja Entitas menemukan bahwa tidak ada baris yang telah diperbarui oleh saat ini Update atau Delete perintah (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 ada sesuatu di baris yang telah berubah sejak baris pertama kali dibaca, Where klausa tidak akan mengembalikan baris untuk diperbarui, yang ditafsirkan oleh Kerangka Kerja Entitas sebagai konflik konkurensi. Metode ini seefektif menggunakan Timestamp bidang, tetapi bisa tidak efisien. Untuk tabel database yang memiliki banyak kolom, tabel dapat menghasilkan klausa yang sangat besar Where , dan dalam aplikasi web, tabel dapat mengharuskan Anda mempertahankan status dalam jumlah besar. Mempertahankan status dalam jumlah besar dapat memengaruhi performa aplikasi karena memerlukan sumber daya server (misalnya, status sesi) atau harus disertakan dalam halaman web itu sendiri (misalnya, status tampilan).

Dalam tutorial ini Anda akan menambahkan penanganan kesalahan untuk konflik konkurensi optimis untuk entitas yang tidak memiliki properti pelacakan ( Department entitas) dan untuk entitas yang memang memiliki properti pelacakan ( OfficeAssignment entitas).

Menangani Konkurensi Optimis Tanpa Properti Pelacakan

Untuk menerapkan konkurensi optimis untuk Department entitas, yang tidak memiliki properti pelacakan (Timestamp), Anda akan menyelesaikan tugas-tugas berikut:

  • Ubah model data untuk mengaktifkan pelacakan konkurensi untuk Department entitas.
  • SchoolRepository Di kelas , tangani pengecualian konkurensi dalam SaveChanges metode .
  • Di halaman Departments.aspx , tangani pengecualian konkurensi dengan menampilkan pesan kepada pengguna yang memperingatkan bahwa perubahan yang dicoba tidak berhasil. Pengguna kemudian dapat melihat nilai saat ini dan mencoba kembali perubahan jika masih diperlukan.

Mengaktifkan Pelacakan Konkurensi dalam Model Data

Di Visual Studio, buka aplikasi web Contoso University yang sedang Anda kerjakan di tutorial sebelumnya dalam seri ini.

Buka SchoolModel.edmx, dan di perancang model data, klik Name kanan properti di Department entitas lalu klik Properti. Di jendela Properti , ubah properti menjadi ConcurrencyModeFixed.

Gambar16

Lakukan hal yang sama untuk properti skalar non-kunci primer lainnya (Budget, , StartDatedan Administrator.) (Anda tidak dapat melakukan ini untuk properti navigasi.) Ini menentukan bahwa setiap kali Kerangka Kerja Entitas menghasilkan Update perintah atau Delete SQL untuk memperbarui Department entitas dalam database, kolom ini (dengan nilai asli) harus disertakan dalam Where klausul. Jika tidak ada baris yang ditemukan saat Update perintah atau Delete dijalankan, Kerangka Kerja Entitas akan memberikan pengecualian konkurensi optimis.

Simpan dan tutup model data.

Menangani Pengecualian Konkurensi dalam DAL

Buka SchoolRepository.cs dan tambahkan pernyataan berikut using untuk System.Data namespace:

using System.Data;

Tambahkan metode baru SaveChanges berikut, yang menangani pengecualian konkurensi optimis:

public void SaveChanges()
{
    try
    {
        context.SaveChanges();
    }
    catch (OptimisticConcurrencyException ocex)
    {
        context.Refresh(RefreshMode.StoreWins, ocex.StateEntries[0].Entity);
        throw ocex;
    }
}

Jika kesalahan konkurensi terjadi ketika metode ini dipanggil, nilai properti entitas dalam memori diganti dengan nilai yang saat ini ada di database. Pengecualian konkurensi ditumbuhi kembali sehingga halaman web dapat menanganinya.

DeleteDepartment Dalam metode dan UpdateDepartment , ganti panggilan yang ada ke context.SaveChanges() dengan panggilan ke SaveChanges() untuk memanggil metode baru.

Menangani Pengecualian Konkurensi di Lapisan Presentasi

Buka Departments.aspx dan tambahkan OnDeleted="DepartmentsObjectDataSource_Deleted" atribut ke DepartmentsObjectDataSource kontrol. Tag pembuka untuk kontrol sekarang akan menyerupai contoh berikut.

<asp:ObjectDataSource ID="DepartmentsObjectDataSource" runat="server" 
        TypeName="ContosoUniversity.BLL.SchoolBL" DataObjectTypeName="ContosoUniversity.DAL.Department" 
        SelectMethod="GetDepartmentsByName" DeleteMethod="DeleteDepartment" UpdateMethod="UpdateDepartment"
        ConflictDetection="CompareAllValues" OldValuesParameterFormatString="orig{0}" 
        OnUpdated="DepartmentsObjectDataSource_Updated" SortParameterName="sortExpression" 
        OnDeleted="DepartmentsObjectDataSource_Deleted" >

DepartmentsGridView Dalam kontrol, tentukan semua kolom tabel dalam DataKeyNames atribut , seperti yang ditunjukkan dalam contoh berikut. Perhatikan bahwa ini akan membuat bidang status tampilan yang sangat besar, yang merupakan salah satu alasan mengapa menggunakan bidang pelacakan umumnya adalah cara yang disukai untuk melacak konflik konkurensi.

<asp:GridView ID="DepartmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="DepartmentsObjectDataSource" 
        DataKeyNames="DepartmentID,Name,Budget,StartDate,Administrator" 
        OnRowUpdating="DepartmentsGridView_RowUpdating"
        OnRowDataBound="DepartmentsGridView_RowDataBound"
        AllowSorting="True" >

Buka Departments.aspx.cs dan tambahkan pernyataan berikut using untuk System.Data namespace:

using System.Data;

Tambahkan metode baru berikut, yang akan Anda panggil dari kontrol Updated sumber data dan Deleted penanganan aktivitas untuk menangani pengecualian konkurensi:

private void CheckForOptimisticConcurrencyException(ObjectDataSourceStatusEventArgs e, string function)
{
    if (e.Exception.InnerException is OptimisticConcurrencyException)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = 
            "The record you attempted to edit or delete was modified by another " +
            "user after you got the original value. The edit or delete operation was canceled " +
            "and the other user's values have been displayed so you can " +
            "determine whether you still want to edit or delete this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Kode ini memeriksa jenis pengecualian, dan jika itu adalah pengecualian konkurensi, kode secara dinamis membuat CustomValidator kontrol yang pada gilirannya menampilkan pesan dalam ValidationSummary kontrol.

Panggil metode baru dari penanganan Updated aktivitas yang Anda tambahkan sebelumnya. Selain itu, buat penanganan aktivitas baru Deleted yang memanggil metode yang sama (tetapi tidak melakukan hal lain):

protected void DepartmentsObjectDataSource_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        CheckForOptimisticConcurrencyException(e, "update");
        // ...
    }
}

protected void DepartmentsObjectDataSource_Deleted(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        CheckForOptimisticConcurrencyException(e, "delete");
    }
}

Menguji Konkurensi Optimis di Halaman Departemen

Jalankan halaman Departments.aspx .

Cuplikan layar yang memperlihatkan halaman Departemen.

Klik Edit dalam baris dan ubah nilai di kolom Anggaran . (Ingat bahwa Anda hanya bisa mengedit rekaman yang telah Anda buat untuk tutorial ini, karena rekaman database yang ada School berisi beberapa data yang tidak valid. Catatan untuk departemen Ekonomi adalah catatan yang aman untuk dieksperimen.)

Gambar18

Buka jendela browser baru dan jalankan halaman lagi (salin URL dari kotak alamat jendela browser pertama ke jendela browser kedua).

Cuplikan layar yang menunjukkan jendela browser baru yang siap untuk input.

Klik Edit di baris yang sama dengan yang Anda edit sebelumnya dan ubah nilai Anggaran menjadi sesuatu yang berbeda.

Gambar19

Di jendela browser kedua, klik Perbarui. Jumlah Anggaran berhasil diubah ke nilai baru ini.

Gambar20

Di jendela browser pertama, klik Perbarui. Pembaruan gagal. Jumlah Anggaran diputar ulang menggunakan nilai yang Anda tetapkan di jendela browser kedua, dan Anda melihat pesan kesalahan.

Gambar21

Menangani Konkurensi Optimis Menggunakan Properti Pelacakan

Untuk menangani konkurensi optimis untuk entitas yang memiliki properti pelacakan, Anda akan menyelesaikan tugas berikut:

  • Tambahkan prosedur tersimpan ke model data untuk mengelola OfficeAssignment entitas. (Properti pelacakan dan prosedur tersimpan tidak harus digunakan bersama-sama; properti tersebut hanya dikelompokkan bersama di sini untuk ilustrasi.)
  • Tambahkan metode CRUD ke DAL dan BLL untuk OfficeAssignment entitas, termasuk kode untuk menangani pengecualian konkurensi optimis dalam DAL.
  • Buat halaman web penugasan kantor.
  • Uji konkurensi optimis di halaman web baru.

Menambahkan Prosedur Tersimpan OfficeAssignment ke Model Data

Buka file SchoolModel.edmx di perancang model, klik kanan permukaan desain, dan klik Perbarui Model dari Database. Di tab Tambahkan dari kotak dialog Pilih Objek Database Anda , perluas Prosedur Tersimpan dan pilih tiga OfficeAssignment prosedur tersimpan (lihat cuplikan layar berikut), lalu klik Selesai. (Prosedur tersimpan ini sudah ada di database saat Anda mengunduh atau membuatnya menggunakan skrip.)

Gambar02

OfficeAssignment Klik kanan entitas dan pilih Pemetaan Prosedur Tersimpan.

Gambar03

Atur fungsi Sisipkan, Perbarui, dan Hapus untuk menggunakan prosedur tersimpan terkait. OrigTimestamp Untuk parameter Update fungsi, atur Properti ke Timestamp dan pilih opsi Gunakan Nilai Asli.

Gambar04

Ketika Entity Framework memanggil UpdateOfficeAssignment prosedur tersimpan Timestamp , itu akan meneruskan nilai asli kolom dalam OrigTimestamp parameter . Prosedur tersimpan menggunakan parameter ini dalam klausulnya Where :

ALTER PROCEDURE [dbo].[UpdateOfficeAssignment]
    @InstructorID int,
    @Location nvarchar(50),
    @OrigTimestamp timestamp
    AS
    UPDATE OfficeAssignment SET Location=@Location 
    WHERE InstructorID=@InstructorID AND [Timestamp]=@OrigTimestamp;
    IF @@ROWCOUNT > 0
    BEGIN
        SELECT [Timestamp] FROM OfficeAssignment 
            WHERE InstructorID=@InstructorID;
    END

Prosedur tersimpan Timestamp juga memilih nilai baru kolom setelah pembaruan sehingga Kerangka Kerja Entitas dapat menjaga OfficeAssignment entitas yang berada dalam memori tetap sinkron dengan baris database yang sesuai.

(Perhatikan bahwa prosedur tersimpan untuk menghapus penetapan office tidak memiliki OrigTimestamp parameter. Karena itu, Kerangka Kerja Entitas tidak dapat memverifikasi bahwa entitas tidak berubah sebelum menghapusnya.)

Simpan dan tutup model data.

Menambahkan Metode OfficeAssignment ke DAL

Buka ISchoolRepository.cs dan tambahkan metode CRUD berikut untuk OfficeAssignment kumpulan entitas:

IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression);
void InsertOfficeAssignment(OfficeAssignment OfficeAssignment);
void DeleteOfficeAssignment(OfficeAssignment OfficeAssignment);
void UpdateOfficeAssignment(OfficeAssignment OfficeAssignment, OfficeAssignment origOfficeAssignment);

Tambahkan metode baru berikut ke SchoolRepository.cs. Dalam metode , UpdateOfficeAssignment Anda memanggil metode lokal SaveChanges alih-alih context.SaveChanges.

public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    return new ObjectQuery<OfficeAssignment>("SELECT VALUE o FROM OfficeAssignments AS o", context).Include("Person").OrderBy("it." + sortExpression).ToList();
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    context.OfficeAssignments.AddObject(officeAssignment);
    context.SaveChanges();
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    context.OfficeAssignments.Attach(officeAssignment);
    context.OfficeAssignments.DeleteObject(officeAssignment);
    context.SaveChanges();
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    context.OfficeAssignments.Attach(origOfficeAssignment);
    context.ApplyCurrentValues("OfficeAssignments", officeAssignment);
    SaveChanges();
}

Dalam proyek pengujian, buka MockSchoolRepository.cs dan tambahkan koleksi dan metode CRUD berikut OfficeAssignment ke dalamnya. (Repositori tiruan harus mengimplementasikan antarmuka repositori, atau solusinya tidak akan dikompilasi.)

List<OfficeAssignment> officeAssignments = new List<OfficeAssignment>();
        
public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    return officeAssignments;
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    officeAssignments.Add(officeAssignment);
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    officeAssignments.Remove(officeAssignment);
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    officeAssignments.Remove(origOfficeAssignment);
    officeAssignments.Add(officeAssignment);
}

Menambahkan Metode OfficeAssignment ke BLL

Dalam proyek utama, buka SchoolBL.cs dan tambahkan metode CRUD berikut untuk entitas yang OfficeAssignment diatur ke dalamnya:

public IEnumerable<OfficeAssignment> GetOfficeAssignments(string sortExpression)
{
    if (string.IsNullOrEmpty(sortExpression)) sortExpression = "Person.LastName";
    return schoolRepository.GetOfficeAssignments(sortExpression);
}

public void InsertOfficeAssignment(OfficeAssignment officeAssignment)
{
    try
    {
        schoolRepository.InsertOfficeAssignment(officeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void DeleteOfficeAssignment(OfficeAssignment officeAssignment)
{
    try
    {
        schoolRepository.DeleteOfficeAssignment(officeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

public void UpdateOfficeAssignment(OfficeAssignment officeAssignment, OfficeAssignment origOfficeAssignment)
{
    try
    {
        schoolRepository.UpdateOfficeAssignment(officeAssignment, origOfficeAssignment);
    }
    catch (Exception ex)
    {
        //Include catch blocks for specific exceptions first,
        //and handle or log the error as appropriate in each.
        //Include a generic catch block like this one last.
        throw ex;
    }
}

Membuat Halaman Web OfficeAssignments

Buat halaman web baru yang menggunakan halaman master Site.Master dan beri nama OfficeAssignments.aspx. Tambahkan markup berikut ke Content kontrol bernama Content2:

<h2>Office Assignments</h2>
    <asp:ObjectDataSource ID="OfficeAssignmentsObjectDataSource" runat="server" TypeName="ContosoUniversity.BLL.SchoolBL"
        DataObjectTypeName="ContosoUniversity.DAL.OfficeAssignment" SelectMethod="GetOfficeAssignments"
        DeleteMethod="DeleteOfficeAssignment" UpdateMethod="UpdateOfficeAssignment" ConflictDetection="CompareAllValues"
        OldValuesParameterFormatString="orig{0}"
        SortParameterName="sortExpression"  OnUpdated="OfficeAssignmentsObjectDataSource_Updated">
    </asp:ObjectDataSource>
    <asp:ValidationSummary ID="OfficeAssignmentsValidationSummary" runat="server" ShowSummary="true"
        DisplayMode="BulletList" Style="color: Red; width: 40em;" />
    <asp:GridView ID="OfficeAssignmentsGridView" runat="server" AutoGenerateColumns="False"
        DataSourceID="OfficeAssignmentsObjectDataSource" DataKeyNames="InstructorID,Timestamp"
        AllowSorting="True">
        <Columns>
            <asp:CommandField ShowEditButton="True" ShowDeleteButton="True" ItemStyle-VerticalAlign="Top">
                <ItemStyle VerticalAlign="Top"></ItemStyle>
            </asp:CommandField>
            <asp:TemplateField HeaderText="Instructor" SortExpression="Person.LastName">
                <ItemTemplate>
                    <asp:Label ID="InstructorLastNameLabel" runat="server" Text='<%# Eval("Person.LastName") %>'></asp:Label>,
                    <asp:Label ID="InstructorFirstNameLabel" runat="server" Text='<%# Eval("Person.FirstMidName") %>'></asp:Label>
                </ItemTemplate>
            </asp:TemplateField>
            <asp:DynamicField DataField="Location" HeaderText="Location" SortExpression="Location"/>
        </Columns>
        <SelectedRowStyle BackColor="LightGray"></SelectedRowStyle>
    </asp:GridView>

Perhatikan bahwa dalam DataKeyNames atribut , markup menentukan Timestamp properti serta kunci rekaman (InstructorID). Menentukan properti dalam DataKeyNames atribut menyebabkan kontrol menyimpannya dalam status kontrol (yang mirip dengan status tampilan) sehingga nilai asli tersedia selama pemrosesan postback.

Jika Anda tidak menyimpan Timestamp nilai, Kerangka Kerja Entitas tidak akan memilikinya untuk Where klausa perintah SQL Update . Akibatnya tidak ada yang akan ditemukan untuk diperbarui. Akibatnya, Kerangka Kerja Entitas akan memberikan pengecualian konkurensi optimis setiap kali OfficeAssignment entitas diperbarui.

Buka OfficeAssignments.aspx.cs dan tambahkan pernyataan berikut using untuk lapisan akses data:

using ContosoUniversity.DAL;

Tambahkan metode berikut Page_Init , yang memungkinkan fungsionalitas Data Dinamis. Tambahkan juga handler berikut untuk ObjectDataSource peristiwa kontrol Updated untuk memeriksa kesalahan konkurensi:

protected void Page_Init(object sender, EventArgs e)
{
    OfficeAssignmentsGridView.EnableDynamicData(typeof(OfficeAssignment));
}

protected void OfficeAssignmentsObjectDataSource_Updated(object sender, ObjectDataSourceStatusEventArgs e)
{
    if (e.Exception != null)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = "The record you attempted to " +
            "update has been modified by another user since you last visited this page. " +
            "Your update was canceled to allow you to review the other user's " +
            "changes and determine if you still want to update this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Menguji Konkurensi Optimis di Halaman OfficeAssignments

Jalankan halaman OfficeAssignments.aspx .

Cuplikan layar yang memperlihatkan halaman Penugasan Office.

Klik Edit dalam baris dan ubah nilai di kolom Lokasi .

Gambar11

Buka jendela browser baru dan jalankan halaman lagi (salin URL dari jendela browser pertama ke jendela browser kedua).

Cuplikan layar yang memperlihatkan jendela browser baru.

Klik Edit di baris yang sama dengan yang Anda edit sebelumnya dan ubah nilai Lokasi menjadi sesuatu yang berbeda.

Gambar12

Di jendela browser kedua, klik Perbarui.

Gambar13

Beralih ke jendela browser pertama dan klik Perbarui.

Gambar15

Anda melihat pesan kesalahan dan nilai Lokasi telah diperbarui untuk memperlihatkan nilai yang Anda ubah di jendela browser kedua.

Menangani Konkurensi dengan Kontrol EntityDataSource

Kontrol EntityDataSource ini mencakup logika bawaan yang mengenali pengaturan konkurensi dalam model data dan menangani operasi pembaruan dan penghapusan yang sesuai. Namun, seperti semua pengecualian, Anda harus menangani OptimisticConcurrencyException pengecualian sendiri untuk memberikan pesan kesalahan yang mudah digunakan.

Selanjutnya, Anda akan mengonfigurasi halaman Courses.aspx (yang menggunakan EntityDataSource kontrol) untuk memungkinkan operasi pembaruan dan penghapusan dan untuk menampilkan pesan kesalahan jika konflik konkurensi terjadi. Entitas Course tidak memiliki kolom pelacakan konkurensi, sehingga Anda akan menggunakan metode yang sama dengan Department entitas: lacak nilai semua properti non-kunci.

Buka file SchoolModel.edmx . Untuk properti Course non-kunci entitas (Title, Credits, dan DepartmentID), atur properti Mode Konkurensi ke Fixed. Kemudian simpan dan tutup model data.

Buka halaman Courses.aspx dan buat perubahan berikut:

  • CoursesEntityDataSource Dalam kontrol, tambahkan EnableUpdate="true" atribut dan EnableDelete="true" . Tag pembuka untuk kontrol tersebut sekarang menyerupai contoh berikut:

    <asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
            ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false" 
            AutoGenerateWhereClause="True" EntitySetName="Courses"
            EnableUpdate="true" EnableDelete="true">
    
  • CoursesGridView Dalam kontrol, ubah nilai atribut menjadi DataKeyNames"CourseID,Title,Credits,DepartmentID". Lalu tambahkan CommandField elemen ke Columns elemen yang memperlihatkan tombol Edit dan Hapus (<asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />). GridView Kontrol sekarang menyerupai contoh berikut:

    <asp:GridView ID="CoursesGridView" runat="server" AutoGenerateColumns="False" 
            DataKeyNames="CourseID,Title,Credits,DepartmentID"
            DataSourceID="CoursesEntityDataSource" >
            <Columns>
                <asp:CommandField ShowEditButton="True" ShowDeleteButton="True" />
                <asp:BoundField DataField="CourseID" HeaderText="CourseID" ReadOnly="True" SortExpression="CourseID" />
                <asp:BoundField DataField="Title" HeaderText="Title" SortExpression="Title" />
                <asp:BoundField DataField="Credits" HeaderText="Credits" SortExpression="Credits" />
            </Columns>
        </asp:GridView>
    

Jalankan halaman dan buat situasi konflik seperti yang Anda lakukan sebelumnya di halaman Departemen. Jalankan halaman di dua jendela browser, klik Edit di baris yang sama di setiap jendela, dan buat perubahan yang berbeda di masing-masing jendela. Klik Perbarui dalam satu jendela lalu klik Perbarui di jendela lain. Saat Anda mengklik Perbarui kedua kalinya, Anda akan melihat halaman kesalahan yang dihasilkan dari pengecualian konkurensi yang tidak tertangani.

Gambar22

Anda menangani kesalahan ini dengan cara yang sangat mirip dengan cara Anda menanganinya untuk ObjectDataSource kontrol. Buka halaman Courses.aspx , dan di CoursesEntityDataSource kontrol, tentukan handler untuk Deleted peristiwa dan Updated . Tag pembuka kontrol sekarang menyerupai contoh berikut:

<asp:EntityDataSource ID="CoursesEntityDataSource" runat="server" 
        ContextTypeName="ContosoUniversity.DAL.SchoolEntities" EnableFlattening="false"
        AutoGenerateWhereClause="true" EntitySetName="Courses" 
        EnableUpdate="true" EnableDelete="true" 
        OnDeleted="CoursesEntityDataSource_Deleted" 
        OnUpdated="CoursesEntityDataSource_Updated">

CoursesGridView Sebelum kontrol, tambahkan kontrol berikutValidationSummary:

<asp:ValidationSummary ID="CoursesValidationSummary" runat="server" 
        ShowSummary="true" DisplayMode="BulletList"  />

Di Courses.aspx.cs, tambahkan using pernyataan untuk System.Data namespace, tambahkan metode yang memeriksa pengecualian konkurensi, dan tambahkan handler untuk EntityDataSource kontrol Updated dan Deleted handler. Kode akan terlihat seperti berikut:

using System.Data;
protected void CoursesEntityDataSource_Updated(object sender, EntityDataSourceChangedEventArgs e)
{
    CheckForOptimisticConcurrencyException(e, "update");
}

protected void CoursesEntityDataSource_Deleted(object sender, EntityDataSourceChangedEventArgs e)
{
    CheckForOptimisticConcurrencyException(e, "delete");
}

private void CheckForOptimisticConcurrencyException(EntityDataSourceChangedEventArgs e, string function)
{
    if (e.Exception != null && e.Exception is OptimisticConcurrencyException)
    {
        var concurrencyExceptionValidator = new CustomValidator();
        concurrencyExceptionValidator.IsValid = false;
        concurrencyExceptionValidator.ErrorMessage = 
            "The record you attempted to edit or delete was modified by another " +
            "user after you got the original value. The edit or delete operation was canceled " +
            "and the other user's values have been displayed so you can " +
            "determine whether you still want to edit or delete this record.";
        Page.Validators.Add(concurrencyExceptionValidator);
        e.ExceptionHandled = true;
    }
}

Satu-satunya perbedaan antara kode ini dan apa yang Anda lakukan untuk ObjectDataSource kontrol adalah bahwa dalam hal ini pengecualian konkurensi ada di Exception properti objek argumen peristiwa daripada di properti pengecualian tersebut InnerException .

Jalankan halaman dan buat konflik konkurensi lagi. Kali ini Anda melihat pesan kesalahan:

Gambar23

Ini menyelesaikan pengenalan penanganan konflik konkurensi. Tutorial berikutnya akan memberikan panduan tentang cara meningkatkan performa dalam aplikasi web yang menggunakan Kerangka Kerja Entitas.