Bağlan dayanıklılık ve yeniden deneme mantığı

Dekont

Yalnızca EF6'ya Doğru - Bu sayfada ele alınan özellikler, API'ler vb. Entity Framework 6'da sunulmuştur. Önceki bir sürümü kullanıyorsanız, bilgilerin bir kısmı veya tümü geçerli değildir.

Bir veritabanı sunucusuna bağlanan uygulamalar, arka uç hataları ve ağ kararlılığı nedeniyle her zaman bağlantı kesintilerine karşı savunmasızdır. Ancak, ayrılmış veritabanı sunucularında çalışan LAN tabanlı bir ortamda bu hatalar, bu hataları işlemek için fazladan mantık gerekmeyecek kadar nadirdir. Windows Azure SQL Veritabanı gibi bulut tabanlı veritabanı sunucularının ve daha az güvenilir ağlar üzerindeki bağlantıların artmasıyla, bağlantı kesintilerinin ortaya çıkması artık daha yaygın bir durumdur. Bunun nedeni, bulut veritabanlarının bağlantı azaltma gibi hizmetin eşit olmasını sağlamak için kullandığı savunma tekniklerinden veya aralıklı zaman aşımlarına ve diğer geçici hatalara neden olan ağda istikrarsızlık olabilir.

Bağlan Ion Resiliency, EF'nin bu bağlantı kesintileri nedeniyle başarısız olan komutları otomatik olarak yeniden deneme özelliğini ifade eder.

Yürütme Stratejileri

Bağlan ion yeniden denemesi, IDbExecutionStrategy arabiriminin bir uygulaması tarafından kullanılmaktadır. IDbExecutionStrategy uygulamaları bir işlemi kabul etmekle ve bir özel durum oluşursa, yeniden denemenin uygun olup olmadığını belirlemek ve uygunsa yeniden denemekle sorumludur. EF ile birlikte gelen dört yürütme stratejisi vardır:

  1. DefaultExecutionStrategy: Bu yürütme stratejisi hiçbir işlemi yeniden denemez, sql server dışındaki veritabanları için varsayılandır.
  2. DefaultSqlExecutionStrategy: Bu, varsayılan olarak kullanılan bir iç yürütme stratejisidir. Bu strateji hiç yeniden denemez, ancak kullanıcıları bağlantı dayanıklılığını etkinleştirmek isteyebileceği konusunda bilgilendirmek için geçici olabilecek özel durumları sarmalar.
  3. DbExecutionStrategy: Bu sınıf, kendi özel sınıflarınız da dahil olmak üzere diğer yürütme stratejileri için temel sınıf olarak uygundur. İlk yeniden denemenin sıfır gecikmeyle gerçekleştiği ve maksimum yeniden deneme sayısına isabet edene kadar gecikmenin katlanarak arttığı bir üstel yeniden deneme ilkesi uygular. Bu sınıf, hangi özel durumların yeniden denenmesi gerektiğini denetlemek için türetilmiş yürütme stratejilerinde uygulanabilen soyut bir ShouldRetryOn yöntemine sahiptir.
  4. SqlAzureExecutionStrategy: Bu yürütme stratejisi DbExecutionStrategy'den devralınır ve Azure SQL Veritabanı ile çalışırken geçici olduğu bilinen özel durumları yeniden dener.

Dekont

Yürütme stratejileri 2 ve 4, EntityFramework.SqlServer derlemesinde yer alan ve SQL Server ile çalışacak şekilde tasarlanmış EF ile birlikte gelen Sql Server sağlayıcısına dahil edilir.

Yürütme Stratejisini Etkinleştirme

EF'e yürütme stratejisi kullanmasını söylemenin en kolay yolu, DbConfiguration sınıfının SetExecutionStrategy yöntemidir:

public class MyConfiguration : DbConfiguration
{
    public MyConfiguration()
    {
        SetExecutionStrategy("System.Data.SqlClient", () => new SqlAzureExecutionStrategy());
    }
}

Bu kod, EF'ye SQL Server'a bağlanırken SqlAzureExecutionStrategy kullanmasını söyler.

Yürütme Stratejisini Yapılandırma

SqlAzureExecutionStrategy oluşturucu, MaxRetryCount ve MaxDelay adlı iki parametreyi kabul edebilir. MaxRetry sayısı, stratejinin yeniden deneme sayısı üst sınırıdır. MaxDelay, yürütme stratejisinin kullanacağı yeniden denemeler arasındaki en uzun gecikme süresini temsil eden bir TimeSpan'dır.

Yeniden deneme sayısı üst sınırını 1 ve en fazla gecikmeyi 30 saniye olarak ayarlamak için aşağıdakileri yürütebilirsiniz:

public class MyConfiguration : DbConfiguration
{
    public MyConfiguration()
    {
        SetExecutionStrategy(
            "System.Data.SqlClient",
            () => new SqlAzureExecutionStrategy(1, TimeSpan.FromSeconds(30)));
    }
}

SqlAzureExecutionStrategy, geçici bir hata oluştuğunda anında yeniden dener, ancak en fazla yeniden deneme sınırı aşılana veya toplam süre maksimum gecikmeye ulaşana kadar her yeniden deneme arasında daha uzun süre geciktirilir.

Yürütme stratejileri yalnızca genellikle geçici olan sınırlı sayıda özel durumu yeniden dener; yine de diğer hataları işlemeniz ve bir hatanın geçici olmadığı veya kendisinin çözülmesinin çok uzun sürdüğü durum için RetryLimitExceeded özel durumunu yakalamanız gerekir.

Yeniden deneme yürütme stratejisi kullanılırken bazı bilinen sınırlamalar vardır:

Akış sorguları desteklenmiyor

Varsayılan olarak, EF6 ve sonraki sürüm, sorgu sonuçlarını akışa almak yerine arabelleğe alır. Sonuçların akışa alınmasını istiyorsanız, LinQ değerini Varlıklar olarak değiştirmek için AsStreaming yöntemini kullanarak akış yapabilirsiniz.

using (var db = new BloggingContext())
{
    var query = (from b in db.Blogs
                orderby b.Url
                select b).AsStreaming();
    }
}

Yeniden deneme yürütme stratejisi kaydedildiğinde akış desteklenmez. Bu sınırlamanın nedeni, bağlantının döndürülen sonuçlarda bir kısmını bırakabileceğidir. Bu durumda EF'nin sorgunun tamamını yeniden çalıştırması gerekir ancak hangi sonuçların döndürülmüş olduğunu bilmenin güvenilir bir yolu yoktur (ilk sorgu gönderildikten sonra veriler değişmiş olabilir, sonuçlar farklı bir sırada geri gelebilir, sonuçların benzersiz tanımlayıcısı olmayabilir vb.).

Kullanıcı tarafından başlatılan işlemler desteklenmiyor

Yeniden denemelerle sonuçlanan bir yürütme stratejisi yapılandırdığınızda, işlemlerin kullanımıyla ilgili bazı sınırlamalar vardır.

Varsayılan olarak EF, bir işlem içindeki tüm veritabanı güncelleştirmelerini gerçekleştirir. Bunu etkinleştirmek için hiçbir şey yapmanız gerekmez, EF bunu her zaman otomatik olarak yapar.

Örneğin, aşağıdaki kodda SaveChanges otomatik olarak bir işlem içinde gerçekleştirilir. Yeni Sitelerden biri eklendikten sonra SaveChanges başarısız olursa işlem geri alınır ve veritabanına hiçbir değişiklik uygulanmaz. Bağlam, değişiklikleri uygulamayı yeniden denemek için SaveChanges'in yeniden çağrılmasını sağlayan bir durumda da bırakılır.

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();
}

Yeniden deneme yürütme stratejisi kullanmadığınızda, tek bir işlemde birden çok işlemi sarmalayabilirsiniz. Örneğin, aşağıdaki kod tek bir işlemde iki SaveChanges çağrısını sarmalar. her iki işlemin herhangi bir bölümü başarısız olursa, değişikliklerin hiçbiri uygulanmaz.

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();
    }
}

EF önceki işlemlerin ve bunların nasıl yeniden denendiğinin farkında olmadığından, yeniden deneme yürütme stratejisi kullanılırken bu desteklenmez. Örneğin, ikinci SaveChanges başarısız olursa EF artık ilk SaveChanges çağrısını yeniden denemek için gerekli bilgilere sahip değildir.

Çözüm: Yürütme Stratejisini El ile Çağırma

Çözüm, yürütme stratejisini el ile kullanmak ve işlemlerden biri başarısız olursa her şeyi yeniden deneyebilmesi için çalıştırılacak tüm mantık kümesini vermektir. DbExecutionStrategy'den türetilen bir yürütme stratejisi çalıştırıldığında, SaveChanges'te kullanılan örtük yürütme stratejisini askıya alır.

Yeniden denenecek kod bloğu içinde tüm bağlamların oluşturulmalıdır. Bu, her yeniden deneme için temiz bir durumla başlamamızı sağlar.

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();
            }
        }
    });