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.
Konkurensi optimis melibatkan upaya optimis untuk menyimpan entitas Anda ke database dengan harapan bahwa data di sana tidak berubah sejak entitas dimuat. Jika ternyata data telah berubah, maka pengecualian dilemparkan dan Anda harus mengatasi konflik sebelum mencoba menyimpan lagi. Topik ini membahas cara menangani pengecualian tersebut dalam Kerangka Kerja Entitas. Teknik yang ditunjukkan dalam topik ini berlaku sama untuk model yang dibuat dengan Code First dan EF Designer.
Posting ini bukan tempat yang tepat untuk diskusi lengkap tentang konkurensi optimis. Bagian di bawah ini mengasumsikan beberapa pengetahuan tentang resolusi konkurensi dan menunjukkan pola untuk tugas umum.
Banyak dari pola ini menggunakan topik yang dibahas dalam Bekerja dengan Nilai Properti.
Mengatasi masalah konkurensi saat Anda menggunakan asosiasi independen (di mana kunci asing tidak dipetakan ke properti di entitas Anda) jauh lebih sulit daripada ketika Anda menggunakan asosiasi kunci asing. Oleh karena itu jika Anda akan melakukan resolusi konkurensi dalam aplikasi Anda, disarankan agar Anda selalu memetakan kunci asing ke dalam entitas Anda. Semua contoh di bawah ini mengasumsikan bahwa Anda menggunakan asosiasi kunci asing.
DbUpdateConcurrencyException dilemparkan oleh SaveChanges ketika pengecualian konkurensi optimis terdeteksi saat mencoba menyimpan entitas yang menggunakan asosiasi kunci asing.
Menyelesaikan pengecualian konkurensi optimis dengan Reload (database menang)
Metode Muat Ulang dapat digunakan untuk mengganti nilai entitas saat ini dengan nilai terbaru dalam database. Entitas kemudian biasanya diberikan kembali kepada pengguna dalam beberapa formulir dan mereka harus mencoba membuat perubahan mereka lagi dan menyimpan ulang. Contohnya:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
blog.Name = "The New ADO.NET Blog";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update the values of the entity that failed to save from the store
ex.Entries.Single().Reload();
}
} while (saveFailed);
}
Cara yang baik untuk mensimulasikan pengecualian konkurensi adalah dengan mengatur titik henti pada panggilan SaveChanges lalu memodifikasi entitas yang sedang disimpan dalam database menggunakan alat lain seperti SQL Server Management Studio. Anda juga dapat menyisipkan baris sebelum SaveChanges untuk memperbarui database secara langsung menggunakan SqlCommand. Contohnya:
context.Database.SqlCommand(
"UPDATE dbo.Blogs SET Name = 'Another Name' WHERE BlogId = 1");
Metode Entri pada DbUpdateConcurrencyException mengembalikan instans DbEntityEntry untuk entitas yang gagal diperbarui. (Properti ini saat ini selalu mengembalikan satu nilai untuk masalah konkurensi. Ini dapat mengembalikan beberapa nilai untuk pengecualian pembaruan umum.) Alternatif untuk beberapa situasi mungkin adalah mendapatkan entri untuk semua entitas yang mungkin perlu dimuat ulang dari database dan memanggil muat ulang untuk masing-masing entitas ini.
Menyelesaikan pengecualian konkurensi optimis saat klien menang
Contoh di atas yang menggunakan Reload terkadang disebut "database wins" atau "store wins" karena nilai dalam entitas ditimpa oleh nilai dari database. Terkadang Anda mungkin ingin melakukan kebalikannya dan menimpa nilai dalam database dengan nilai yang saat ini ada di entitas. Ini kadang-kadang disebut "client wins" dan dapat dilakukan dengan cara mendapatkan nilai nilai saat ini dari database dan mengaturnya sebagai nilai asli untuk entitas tersebut. (Lihat Bekerja dengan Nilai Properti untuk informasi tentang nilai saat ini dan asli.) Misalnya:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
blog.Name = "The New ADO.NET Blog";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Update original values from the database
var entry = ex.Entries.Single();
entry.OriginalValues.SetValues(entry.GetDatabaseValues());
}
} while (saveFailed);
}
Resolusi khusus untuk pengecualian konkurensi optimistis
Terkadang Anda mungkin ingin menggabungkan nilai yang saat ini ada di database dengan nilai yang saat ini ada di entitas. Ini biasanya memerlukan beberapa logika kustom atau interaksi pengguna. Misalnya, Anda dapat menyajikan formulir kepada pengguna yang berisi nilai saat ini, nilai dalam database, dan sekumpulan nilai terselesaikan default. Pengguna kemudian akan mengedit nilai yang diselesaikan seperlunya dan itu akan menjadi nilai yang diselesaikan ini yang disimpan ke database. Ini dapat dilakukan menggunakan objek DbPropertyValues yang dikembalikan dari CurrentValues dan GetDatabaseValues pada entri entitas. Contohnya:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
blog.Name = "The New ADO.NET Blog";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Get the current entity values and the values in the database
var entry = ex.Entries.Single();
var currentValues = entry.CurrentValues;
var databaseValues = entry.GetDatabaseValues();
// Choose an initial set of resolved values. In this case we
// make the default be the values currently in the database.
var resolvedValues = databaseValues.Clone();
// Have the user choose what the resolved values should be
HaveUserResolveConcurrency(currentValues, databaseValues, resolvedValues);
// Update the original values with the database values and
// the current values with whatever the user choose.
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(resolvedValues);
}
} while (saveFailed);
}
public void HaveUserResolveConcurrency(DbPropertyValues currentValues,
DbPropertyValues databaseValues,
DbPropertyValues resolvedValues)
{
// Show the current, database, and resolved values to the user and have
// them edit the resolved values to get the correct resolution.
}
Resolusi kustom pengecualian penggandengan optimis dengan penggunaan objek
Kode di atas menggunakan instans DbPropertyValues untuk mengedarkan nilai saat ini, nilai dalam database, dan nilai yang terselesaikan. Terkadang mungkin lebih mudah untuk menggunakan contoh jenis entitas Anda untuk ini. Ini dapat dilakukan menggunakan metode ToObject dan SetValues dari DbPropertyValues. Contohnya:
using (var context = new BloggingContext())
{
var blog = context.Blogs.Find(1);
blog.Name = "The New ADO.NET Blog";
bool saveFailed;
do
{
saveFailed = false;
try
{
context.SaveChanges();
}
catch (DbUpdateConcurrencyException ex)
{
saveFailed = true;
// Get the current entity values and the values in the database
// as instances of the entity type
var entry = ex.Entries.Single();
var databaseValues = entry.GetDatabaseValues();
var databaseValuesAsBlog = (Blog)databaseValues.ToObject();
// Choose an initial set of resolved values. In this case we
// make the default be the values currently in the database.
var resolvedValuesAsBlog = (Blog)databaseValues.ToObject();
// Have the user choose what the resolved values should be
HaveUserResolveConcurrency((Blog)entry.Entity,
databaseValuesAsBlog,
resolvedValuesAsBlog);
// Update the original values with the database values and
// the current values with whatever the user choose.
entry.OriginalValues.SetValues(databaseValues);
entry.CurrentValues.SetValues(resolvedValuesAsBlog);
}
} while (saveFailed);
}
public void HaveUserResolveConcurrency(Blog entity,
Blog databaseValues,
Blog resolvedValues)
{
// Show the current, database, and resolved values to the user and have
// them update the resolved values to get the correct resolution.
}