Ketahanan koneksi dan logika coba lagi
Catatan
Hanya EF6 Dan seterusnya - Fitur, API, dll. yang dibahas di halaman ini diperkenalkan dalam Kerangka Kerja Entitas 6. Jika Anda menggunakan versi yang lebih lama, beberapa atau semua informasi tidak berlaku.
Aplikasi yang terhubung ke server database selalu rentan terhadap pemutusan koneksi karena kegagalan back-end dan ketidakstabilan jaringan. Namun, dalam lingkungan berbasis LAN yang bekerja terhadap server database khusus, kesalahan ini cukup langka sehingga logika tambahan untuk menangani kegagalan tersebut tidak sering diperlukan. Dengan munculnya server database berbasis cloud seperti Windows Azure SQL Database dan koneksi melalui jaringan yang kurang andal sekarang lebih umum untuk terjadinya pemutusan koneksi. Ini bisa disebabkan oleh teknik defensif yang digunakan database cloud untuk memastikan kewajaran layanan, seperti pembatasan koneksi, atau ketidakstabilan dalam jaringan yang menyebabkan batas waktu terputus-terputus dan kesalahan sementara lainnya.
Ketahanan Koneksi mengacu pada kemampuan EF untuk secara otomatis mencoba kembali perintah apa pun yang gagal karena pemutusan koneksi ini.
Strategi Eksekusi
Coba lagi koneksi ditangani oleh implementasi antarmuka IDbExecutionStrategy. Implementasi IDbExecutionStrategy akan bertanggung jawab untuk menerima operasi dan, jika pengecualian terjadi, menentukan apakah percobaan ulang sesuai dan mencoba kembali jika benar. Ada empat strategi eksekusi yang dikirimkan dengan EF:
- DefaultExecutionStrategy: strategi eksekusi ini tidak mencoba kembali operasi apa pun, ini adalah default untuk database selain server sql.
- DefaultSqlExecutionStrategy: ini adalah strategi eksekusi internal yang digunakan secara default. Strategi ini tidak mencoba lagi sama sekali, namun, strategi ini akan membungkus pengecualian apa pun yang dapat bersifat sementara untuk memberi tahu pengguna bahwa mereka mungkin ingin mengaktifkan ketahanan koneksi.
- DbExecutionStrategy: kelas ini cocok sebagai kelas dasar untuk strategi eksekusi lainnya, termasuk yang kustom Anda sendiri. Ini menerapkan kebijakan coba lagi eksponensial, di mana percobaan ulang awal terjadi dengan penundaan nol dan penundaan meningkat secara eksponensial sampai jumlah percobaan kembali maksimum terpukul. Kelas ini memiliki metode ShouldRetryOn abstrak yang dapat diterapkan dalam strategi eksekusi turunan untuk mengontrol pengecualian mana yang harus dicoba kembali.
- SqlAzureExecutionStrategy: strategi eksekusi ini mewarisi dari DbExecutionStrategy dan akan mencoba kembali pengecualian yang mungkin sementara saat bekerja dengan Azure SQL Database.
Catatan
Strategi eksekusi 2 dan 4 termasuk dalam penyedia Sql Server yang dikirimkan dengan EF, yang berada dalam rakitan EntityFramework.SqlServer dan dirancang untuk bekerja dengan SQL Server.
Mengaktifkan Strategi Eksekusi
Cara termudah untuk memberi tahu EF untuk menggunakan strategi eksekusi adalah dengan metode SetExecutionStrategy dari kelas DbConfiguration :
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
}
}
Kode ini memberi tahu EF untuk menggunakan SqlAzureExecutionStrategy saat menyambungkan ke SQL Server.
Mengonfigurasi Strategi Eksekusi
Konstruktor SqlAzureExecutionStrategy dapat menerima dua parameter, MaxRetryCount dan MaxDelay. Jumlah MaxRetry adalah berapa kali strategi akan mencoba kembali. MaxDelay adalah TimeSpan yang mewakili penundaan maksimum antara percobaan ulang yang akan digunakan strategi eksekusi.
Untuk mengatur jumlah maksimum percobaan ulang ke 1 dan penundaan maksimum menjadi 30 detik, Anda akan menjalankan hal berikut:
public class MyConfiguration : DbConfiguration
{
public MyConfiguration()
{
SetExecutionStrategy(
"System.Data.SqlClient",
() => new SqlAzureExecutionStrategy(1, TimeSpan.FromSeconds(30)));
}
}
SqlAzureExecutionStrategy akan mencoba kembali secara instan saat pertama kali kegagalan sementara terjadi, tetapi akan menunda lebih lama antara setiap coba lagi sampai batas percobaan kembali maksimum terlampaui atau total waktu mencapai penundaan maksimum.
Strategi eksekusi hanya akan mencoba kembali sejumlah pengecualian terbatas yang biasanya sementara, Anda masih perlu menangani kesalahan lain serta menangkap pengecualian RetryLimitExceeded untuk kasus di mana kesalahan tidak sementara atau membutuhkan waktu terlalu lama untuk menyelesaikannya sendiri.
Ada beberapa batasan yang diketahui saat menggunakan strategi eksekusi coba lagi:
Kueri streaming tidak didukung
Secara default, EF6 dan versi yang lebih baru akan menyangga hasil kueri daripada melakukan streaming. Jika Anda ingin hasil yang dialirkan, Anda dapat menggunakan metode AsStreaming untuk mengubah kueri LINQ ke Entitas ke streaming.
using (var db = new BloggingContext())
{
var query = (from b in db.Blogs
orderby b.Url
select b).AsStreaming();
}
}
Streaming tidak didukung saat strategi eksekusi coba lagi didaftarkan. Batasan ini ada karena koneksi dapat menjatuhkan sebagian melalui hasil yang dikembalikan. Ketika ini terjadi, EF perlu menjalankan kembali seluruh kueri tetapi tidak memiliki cara yang dapat diandalkan untuk mengetahui hasil mana yang telah dikembalikan (data mungkin telah berubah sejak kueri awal dikirim, hasilnya mungkin kembali dalam urutan yang berbeda, hasil mungkin tidak memiliki pengidentifikasi unik, dll.).
Transaksi yang dimulai pengguna tidak didukung
Ketika Anda telah mengonfigurasi strategi eksekusi yang menghasilkan percobaan ulang, ada beberapa batasan sekeliling penggunaan transaksi.
Secara default, EF akan melakukan pembaruan database apa pun dalam transaksi. Anda tidak perlu melakukan apa pun untuk mengaktifkan ini, EF selalu melakukan ini secara otomatis.
Misalnya, dalam kode berikut SaveChanges secara otomatis dilakukan dalam transaksi. Jika SaveChanges gagal setelah menyisipkan salah satu Situs baru maka transaksi akan digulung balik dan tidak ada perubahan yang diterapkan pada database. Konteks juga dibiarkan dalam keadaan yang memungkinkan SaveChanges dipanggil lagi untuk mencoba kembali menerapkan perubahan.
using (var db = new BloggingContext())
{
db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
db.Blogs.Add(new Site { Url = "http://blogs.msdn.com/adonet" });
db.SaveChanges();
}
Saat tidak menggunakan strategi eksekusi coba lagi, Anda dapat membungkus beberapa operasi dalam satu transaksi. Misalnya, kode berikut membungkus dua panggilan SaveChanges dalam satu transaksi. Jika ada bagian dari salah satu operasi yang gagal, maka tidak ada perubahan yang diterapkan.
using (var db = new BloggingContext())
{
using (var trn = db.Database.BeginTransaction())
{
db.Blogs.Add(new Site { Url = "http://msdn.com/data/ef" });
db.Blogs.Add(new Site { Url = "http://blogs.msdn.com/adonet" });
db.SaveChanges();
db.Blogs.Add(new Site { Url = "http://twitter.com/efmagicunicorns" });
db.SaveChanges();
trn.Commit();
}
}
Ini tidak didukung saat menggunakan strategi eksekusi coba lagi karena EF tidak mengetahui operasi sebelumnya dan cara mencobanya kembali. Misalnya, jika SaveChanges kedua gagal, maka EF tidak lagi memiliki informasi yang diperlukan untuk mencoba kembali panggilan SaveChanges pertama.
Solusi: Memanggil Strategi Eksekusi Secara Manual
Solusinya adalah menggunakan strategi eksekusi secara manual dan memberinya seluruh set logika yang akan dijalankan, sehingga dapat mencoba kembali semuanya jika salah satu operasi gagal. Ketika strategi eksekusi yang berasal dari DbExecutionStrategy berjalan, strategi eksekusi implisit yang digunakan dalam SaveChanges akan ditangguhkan.
Perhatikan bahwa konteks apa pun harus dibangun dalam blok kode untuk dicoba kembali. Ini memastikan bahwa kita dimulai dengan status bersih untuk setiap coba lagi.
var executionStrategy = new SqlAzureExecutionStrategy();
executionStrategy.Execute(
() =>
{
using (var db = new BloggingContext())
{
using (var trn = db.Database.BeginTransaction())
{
db.Blogs.Add(new Blog { Url = "http://msdn.com/data/ef" });
db.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/adonet" });
db.SaveChanges();
db.Blogs.Add(new Blog { Url = "http://twitter.com/efmagicunicorns" });
db.SaveChanges();
trn.Commit();
}
}
});