Bağlantı DayanıklılığıConnection Resiliency

Bağlantı dayanıklılığı başarısız veritabanı komutlarını otomatik olarak yeniden dener.Connection resiliency automatically retries failed database commands. Özelliği, hataların algılanması ve yeniden denenmesinin gerekli mantığını kapsülleyen bir "yürütme stratejisi" sağlayarak herhangi bir veritabanı ile birlikte kullanılabilir.The feature can be used with any database by supplying an "execution strategy", which encapsulates the logic necessary to detect failures and retry commands. EF Core sağlayıcılar, belirli veritabanı hata koşulları ve en iyi yeniden deneme ilkelerine uyarlanmış yürütme stratejileri sağlayabilir.EF Core providers can supply execution strategies tailored to their specific database failure conditions and optimal retry policies.

Örnek olarak, SQL Server sağlayıcısı, SQL Server (SQL Azure dahil) özel olarak uyarlanmış bir yürütme stratejisi içerir.As an example, the SQL Server provider includes an execution strategy that is specifically tailored to SQL Server (including SQL Azure). Yeniden denenebilecek ve en fazla yeniden deneme sayısı, yeniden denemeler arasındaki gecikme (vb.) için izin verilen özel durum türlerinden haberdar değildir.It is aware of the exception types that can be retried and has sensible defaults for maximum retries, delay between retries, etc.

İçeriğiniz için seçenekler yapılandırılırken bir yürütme stratejisi belirtildi.An execution strategy is specified when configuring the options for your context. Bu, genellikle türetilmiş bağlamınızın OnConfiguring yöntemidir:This is typically in the OnConfiguring method of your derived context:

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

ASP.NET Core bir uygulama için Startup.cs:or in Startup.cs for an ASP.NET Core application:

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

Özel yürütme stratejisiCustom execution strategy

Varsayılandan herhangi birini değiştirmek istiyorsanız, kendi özel bir yürütme stratejisini kaydetme mekanizması vardır.There is a mechanism to register a custom execution strategy of your own if you wish to change any of the defaults.

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

Yürütme stratejileri ve işlemlerExecution strategies and transactions

Hatalarda otomatik olarak yeniden deneme yapan bir yürütme stratejisi, başarısız olan bir yeniden deneme bloğunda her işlemi kayıttan yürütmeyi gerektirir.An execution strategy that automatically retries on failures needs to be able to play back each operation in a retry block that fails. Yeniden denemeler etkinleştirildiğinde, EF Core aracılığıyla gerçekleştirdiğiniz her işlem kendi yeniden kullanılabilir işlem haline gelir.When retries are enabled, each operation you perform via EF Core becomes its own retriable operation. Diğer bir deyişle, her sorgu ve her bir SaveChanges() çağrısı, geçici bir hata oluşursa birim olarak yeniden denenir.That is, each query and each call to SaveChanges() will be retried as a unit if a transient failure occurs.

Ancak, kodunuz BeginTransaction() kullanarak bir işlem başlatırsa, bir birim olarak değerlendirilmesi gereken kendi işlem grubunuzu tanımlamanız gerekir ve işlem içindeki her şeyin geri yürütülmesi bir hata oluşmasına neden olur.However, if your code initiates a transaction using BeginTransaction() you are defining your own group of operations that need to be treated as a unit, and everything inside the transaction would need to be played back shall a failure occur. Bir yürütme stratejisi kullanırken bunu yapmayı denerseniz, aşağıdaki gibi bir özel durum alırsınız:You will receive an exception like the following if you attempt to do this when using an execution strategy:

InvalidOperationException: yapılandırılan ' Sqlserverretryingexecutionstrateji ' yürütme stratejisi Kullanıcı tarafından başlatılan işlemleri desteklemiyor.InvalidOperationException: The configured execution strategy 'SqlServerRetryingExecutionStrategy' does not support user initiated transactions. İşlemdeki tüm işlemleri yeniden kullanılabilir bir birim olarak yürütmek için ' DbContext. Database. Createexecutionstrateji () ' tarafından döndürülen yürütme stratejisini kullanın.Use the execution strategy returned by 'DbContext.Database.CreateExecutionStrategy()' to execute all the operations in the transaction as a retriable unit.

Çözüm, yürütülmesi gereken her şeyi temsil eden bir temsilciyle yürütme stratejisini el ile çağırmalıdır.The solution is to manually invoke the execution strategy with a delegate representing everything that needs to be executed. Geçici bir hata oluşursa yürütme stratejisi temsilciyi yeniden çağırır.If a transient failure occurs, the execution strategy will invoke the delegate again.

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

Bu yaklaşım, Ambient işlemler ile de kullanılabilir.This approach can also be used with ambient transactions.

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

İşlem işleme hatası ve teklik sorunuTransaction commit failure and the idempotency issue

Genel olarak, bir bağlantı hatası olduğunda geçerli işlem geri alınır.In general, when there is a connection failure the current transaction is rolled back. Ancak, işlem yürütülürken bağlantı kesildiğinde işlemin sonuç durumu bilinmez.However, if the connection is dropped while the transaction is being committed the resulting state of the transaction is unknown.

Varsayılan olarak, yürütme stratejisi işlem geri alınmış gibi işlemi yeniden dener, ancak böyle bir durum yoksa, yeni veritabanı durumu uyumsuzsa ya da işlem belirli bir duruma bağlı değilse veri bozulmasına yol açacağından, otomatik olarak oluşturulan anahtar değerleriyle yeni bir satır eklenirken bir özel durumla sonuçlanacaktır.By default, the execution strategy will retry the operation as if the transaction was rolled back, but if it's not the case this will result in an exception if the new database state is incompatible or could lead to data corruption if the operation does not rely on a particular state, for example when inserting a new row with auto-generated key values.

Bu konuyla başa çıkmak için birkaç yol vardır.There are several ways to deal with this.

Seçenek 1-do (neredeyse) NothingOption 1 - Do (almost) nothing

İşlem işleme sırasında bağlantı hatası olasılığı düşük olduğundan, bu durum aslında gerçekleşirse uygulamanızın başarısız olması için kabul edilebilir hale gelebilir.The likelihood of a connection failure during transaction commit is low so it may be acceptable for your application to just fail if this condition actually occurs.

Ancak, yinelenen bir satır eklemek yerine bir özel durumun yapıldığından emin olmak için mağaza tarafından oluşturulan anahtarları kullanmaktan kaçının.However, you need to avoid using store-generated keys in order to ensure that an exception is thrown instead of adding a duplicate row. İstemci tarafından oluşturulan bir GUID değeri veya bir istemci tarafı değer Oluşturucu kullanmayı düşünün.Consider using a client-generated GUID value or a client-side value generator.

Seçenek 2-uygulama durumunu yeniden derleOption 2 - Rebuild application state

  1. Geçerli DbContextatın.Discard the current DbContext.
  2. Yeni bir DbContext oluşturun ve uygulamanızın durumunu veritabanından geri yükleyin.Create a new DbContext and restore the state of your application from the database.
  3. Kullanıcıya son işlemin başarıyla tamamlanmamış olabileceğini bildirin.Inform the user that the last operation might not have been completed successfully.

Seçenek 3-durum doğrulama eklemeOption 3 - Add state verification

Veritabanı durumunu değiştiren işlemlerin çoğu için başarılı olup olmadığını denetleyen kodu eklemek mümkündür.For most of the operations that change the database state it is possible to add code that checks whether it succeeded. EF bu, daha kolay IExecutionStrategy.ExecuteInTransactionyapmak için bir genişletme yöntemi sağlar.EF provides an extension method to make this easier - IExecutionStrategy.ExecuteInTransaction.

Bu yöntem başlar ve bir işlemi tamamlar ve ayrıca işlem işlemesi sırasında geçici bir hata oluştuğunda çağrılan verifySucceeded parametresinde bir işlevi kabul eder.This method begins and commits a transaction and also accepts a function in the verifySucceeded parameter that is invoked when a transient error occurs during the transaction commit.

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

Not

Burada SaveChanges, Unchanged başarılı olursa Blog varlığının durumunu SaveChanges olarak değiştirmeyi önlemek için acceptAllChangesOnSuccess false olarak ayarlanır.Here SaveChanges is invoked with acceptAllChangesOnSuccess set to false to avoid changing the state of the Blog entity to Unchanged if SaveChanges succeeds. Bu, işleme başarısız olursa ve işlem geri alınırsa aynı işlemi yeniden denemeye izin verir.This allows to retry the same operation if the commit fails and the transaction is rolled back.

Seçenek 4-işlemi el Ile izlemeOption 4 - Manually track the transaction

Mağaza tarafından oluşturulan anahtarları kullanmanız gerekiyorsa veya işleme bağlı olmayan işlem başarısızlıklarını işlemek için genel bir yönteme ihtiyaç duyuyorsanız, işleme başarısız olduğunda denetlenen bir KIMLIK atanabilir.If you need to use store-generated keys or need a generic way of handling commit failures that doesn't depend on the operation performed each transaction could be assigned an ID that is checked when the commit fails.

  1. İşlemin durumunu izlemek için kullanılan veritabanına tablo ekleyin.Add a table to the database used to track the status of the transactions.
  2. Her bir işlemin başındaki tabloya bir satır ekleyin.Insert a row into the table at the beginning of each transaction.
  3. Kayıt sırasında bağlantı başarısız olursa, veritabanında karşılık gelen satırın varolup olmadığını kontrol edin.If the connection fails during the commit, check for the presence of the corresponding row in the database.
  4. Kayıt başarılı olursa, tablonun büyümesini önlemek için karşılık gelen satırı silin.If the commit is successful, delete the corresponding row to avoid the growth of the table.
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();
}

Not

Doğrulama için kullanılan bağlamın bağlantı, işlem kaydı sırasında başarısız olduysa doğrulama sırasında yeniden başarısız olması nedeniyle tanımlanmış bir yürütme stratejisi olduğundan emin olun.Make sure that the context used for the verification has an execution strategy defined as the connection is likely to fail again during verification if it failed during transaction commit.