Bagikan melalui


Ketahanan Sambungan

ketahanan Koneksi ion secara otomatis mencoba kembali perintah database yang gagal. Fitur ini dapat digunakan dengan database apa pun dengan menyediakan "strategi eksekusi", yang merangkum logika yang diperlukan untuk mendeteksi kegagalan dan mencoba kembali perintah. Penyedia EF Core dapat menyediakan strategi eksekusi yang disesuaikan dengan kondisi kegagalan database spesifik mereka dan kebijakan percobaan kembali yang optimal.

Sebagai contoh, penyedia SQL Server menyertakan strategi eksekusi yang khusus disesuaikan dengan SQL Server (termasuk SQL Azure). Ini mengetahui jenis pengecualian yang dapat dicoba kembali dan memiliki default yang masuk akal untuk percobaan ulang maksimum, penundaan antara percobaan ulang, dll.

Strategi eksekusi ditentukan saat mengonfigurasi opsi untuk konteks Anda. Ini biasanya dalam OnConfiguring metode konteks turunan Anda:

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseSqlServer(
            @"Server=(localdb)\mssqllocaldb;Database=EFMiscellanous.ConnectionResiliency;Trusted_Connection=True;ConnectRetryCount=0",
            options => options.EnableRetryOnFailure());
}

atau untuk Startup.cs aplikasi ASP.NET Core:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<PicnicContext>(
        options => options.UseSqlServer(
            "<connection string>",
            providerOptions => providerOptions.EnableRetryOnFailure()));
}

Catatan

Mengaktifkan coba lagi pada kegagalan menyebabkan EF secara internal menyangga hasil, yang dapat secara signifikan meningkatkan persyaratan memori untuk kueri yang mengembalikan hasil besar. Lihat buffering dan streaming untuk detail selengkapnya.

Strategi eksekusi kustom

Ada mekanisme untuk mendaftarkan strategi eksekusi kustom Anda sendiri jika Anda ingin mengubah salah satu default.

protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
    optionsBuilder
        .UseMyProvider(
            "<connection string>",
            options => options.ExecutionStrategy(...));
}

Strategi dan transaksi eksekusi

Strategi eksekusi yang secara otomatis mencoba kembali kegagalan harus dapat memutar kembali setiap operasi dalam blok coba lagi yang gagal. Ketika percobaan ulang diaktifkan, setiap operasi yang Anda lakukan melalui EF Core menjadi operasi yang dapat dicoba kembali sendiri. Artinya, setiap kueri dan setiap panggilan ke SaveChanges() akan dicoba kembali sebagai unit jika kegagalan sementara terjadi.

Namun, jika kode Anda memulai transaksi menggunakan BeginTransaction() Anda mendefinisikan grup operasi Anda sendiri yang perlu diperlakukan sebagai unit, dan semua yang ada di dalam transaksi perlu diputar kembali akan terjadi kegagalan. Anda akan menerima pengecualian seperti berikut ini jika Anda mencoba melakukan ini saat menggunakan strategi eksekusi:

InvalidOperationException: Strategi eksekusi yang dikonfigurasi 'SqlServerRetryingExecutionStrategy' tidak mendukung transaksi yang dimulai pengguna. Gunakan strategi eksekusi yang dikembalikan oleh 'DbContext.Database.CreateExecutionStrategy()' untuk menjalankan semua operasi dalam transaksi sebagai unit yang dapat dicoba kembali.

Solusinya adalah memanggil strategi eksekusi secara manual dengan delegasi yang mewakili semua yang perlu dijalankan. Jika terjadi kegagalan sementara, strategi eksekusi akan memanggil delegasi lagi.


using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();

strategy.Execute(
    () =>
    {
        using var context = new BloggingContext();
        using var transaction = context.Database.BeginTransaction();

        context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
        context.SaveChanges();

        context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
        context.SaveChanges();

        transaction.Commit();
    });

Pendekatan ini juga dapat digunakan dengan transaksi sekitar.


using var context1 = new BloggingContext();
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });

var strategy = context1.Database.CreateExecutionStrategy();

strategy.Execute(
    () =>
    {
        using var context2 = new BloggingContext();
        using var transaction = new TransactionScope();

        context2.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
        context2.SaveChanges();

        context1.SaveChanges();

        transaction.Complete();
    });

Kegagalan penerapan transaksi dan masalah idempotensi

Secara umum, ketika ada kegagalan koneksi, transaksi saat ini digulung balik. Namun, jika koneksi terputus saat transaksi sedang dilakukan, status transaksi yang dihasilkan tidak diketahui.

Secara default, strategi eksekusi akan mencoba kembali operasi seolah-olah transaksi digulung balik, tetapi jika tidak demikian, ini akan menghasilkan pengecualian jika status database baru tidak kompatibel atau dapat menyebabkan kerusakan data jika operasi tidak bergantung pada status tertentu, misalnya saat menyisipkan baris baru dengan nilai kunci yang dihasilkan secara otomatis.

Ada beberapa cara untuk menangani hal ini.

Opsi 1 - Lakukan (hampir) tidak ada

Kemungkinan kegagalan koneksi selama penerapan transaksi rendah sehingga mungkin dapat diterima bagi aplikasi Anda untuk hanya gagal jika kondisi ini benar-benar terjadi.

Namun, Anda perlu menghindari penggunaan kunci yang dihasilkan toko untuk memastikan bahwa pengecualian dilemparkan alih-alih menambahkan baris duplikat. Pertimbangkan untuk menggunakan nilai GUID yang dihasilkan klien atau generator nilai sisi klien.

Opsi 2 - Membangun kembali status aplikasi

  1. Buang .DbContext
  2. Buat baru DbContext dan pulihkan status aplikasi Anda dari database.
  3. Beri tahu pengguna bahwa operasi terakhir mungkin belum berhasil diselesaikan.

Opsi 3 - Tambahkan verifikasi status

Untuk sebagian besar operasi yang mengubah status database dimungkinkan untuk menambahkan kode yang memeriksa apakah operasi berhasil. EF menyediakan metode ekstensi untuk mempermudah ini - IExecutionStrategy.ExecuteInTransaction.

Metode ini memulai dan melakukan transaksi dan juga menerima fungsi dalam verifySucceeded parameter yang dipanggil ketika kesalahan sementara terjadi selama penerapan transaksi.


using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();

var blogToAdd = new Blog { Url = "http://blogs.msdn.com/dotnet" };
db.Blogs.Add(blogToAdd);

strategy.ExecuteInTransaction(
    db,
    operation: context => { context.SaveChanges(acceptAllChangesOnSuccess: false); },
    verifySucceeded: context => context.Blogs.AsNoTracking().Any(b => b.BlogId == blogToAdd.BlogId));

db.ChangeTracker.AcceptAllChanges();

Catatan

Di sini SaveChanges dipanggil dengan acceptAllChangesOnSuccess diatur ke false untuk menghindari perubahan status Blog entitas menjadi Unchanged jika SaveChanges berhasil. Ini memungkinkan untuk mencoba kembali operasi yang sama jika penerapan gagal dan transaksi digulung balik.

Opsi 4 - Melacak transaksi secara manual

Jika Anda perlu menggunakan kunci yang dihasilkan toko atau memerlukan cara umum untuk menangani kegagalan penerapan yang tidak bergantung pada operasi yang dilakukan setiap transaksi dapat ditetapkan ID yang diperiksa saat penerapan gagal.

  1. Tambahkan tabel ke database yang digunakan untuk melacak status transaksi.
  2. Sisipkan baris ke dalam tabel di awal setiap transaksi.
  3. Jika koneksi gagal selama penerapan, periksa keberadaan baris yang sesuai dalam database.
  4. Jika penerapan berhasil, hapus baris yang sesuai untuk menghindari pertumbuhan tabel.

using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();

db.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });

var transaction = new TransactionRow { Id = Guid.NewGuid() };
db.Transactions.Add(transaction);

strategy.ExecuteInTransaction(
    db,
    operation: context => { context.SaveChanges(acceptAllChangesOnSuccess: false); },
    verifySucceeded: context => context.Transactions.AsNoTracking().Any(t => t.Id == transaction.Id));

db.ChangeTracker.AcceptAllChanges();
db.Transactions.Remove(transaction);
db.SaveChanges();

Catatan

Pastikan bahwa konteks yang digunakan untuk verifikasi memiliki strategi eksekusi yang didefinisikan sebagai koneksi kemungkinan akan gagal lagi selama verifikasi jika gagal selama penerapan transaksi.

Sumber Daya Tambahan: