Bağlantı Dayanıklılığı
Bağlantı dayanıklılığı başarısız olan veritabanı komutlarını otomatik olarak yeniden denenir. Bu özellik, hataları algılamak ve komutları yeniden denemek için gerekli mantığı kapsülleyen bir "yürütme stratejisi" sağlayarak herhangi bir veritabanıyla birlikte kullanılabilir. EF Core sağlayıcıları, kendi veritabanı hata koşullarına ve en uygun yeniden deneme ilkelerine göre uyarlanmış yürütme stratejileri sağlayabilir.
Örneğin, SQL Server sağlayıcısı SQL Server'a (SQL Azure dahil) özel olarak uyarlanmış bir yürütme stratejisi içerir. Yeniden denenebilecek özel durum türlerinin farkındadır ve en fazla yeniden deneme sayısı, yeniden denemeler arasındaki gecikme vb. için mantıklı varsayılanlara sahiptir.
Bağlamınız için seçenekler yapılandırılırken bir yürütme stratejisi belirtilir. Bu genellikle türetilmiş bağlamınızın OnConfiguring
yöntemindedir:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=EFMiscellanous.ConnectionResiliency;Trusted_Connection=True;ConnectRetryCount=0",
options => options.EnableRetryOnFailure());
}
veya bir ASP.NET Core uygulamasında Startup.cs
:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PicnicContext>(
options => options.UseSqlServer(
"<connection string>",
providerOptions => providerOptions.EnableRetryOnFailure()));
}
Not
Hatada yeniden denemenin etkinleştirilmesi EF'nin sonuç kümesini dahili olarak arabelleğe almasına neden olur ve bu da büyük sonuç kümeleri döndüren sorgular için bellek gereksinimlerini önemli ölçüde artırabilir. Daha fazla ayrıntı için arabelleğe alma ve akış bölümüne bakın.
Özel yürütme stratejisi
Varsayılanlardan herhangi birini değiştirmek istiyorsanız, kendi özel yürütme stratejinizi kaydetmeye yönelik bir mekanizma vardır.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMyProvider(
"<connection string>",
options => options.ExecutionStrategy(...));
}
Yürütme stratejileri ve işlemleri
Hataları otomatik olarak yeniden deneyen bir yürütme stratejisi, başarısız olan bir yeniden deneme bloğundaki her işlemi yeniden yürütebilmelidir. Yeniden denemeler etkinleştirildiğinde, EF Core aracılığıyla gerçekleştirdiğiniz her işlem yeniden denenebilir bir işlem olur. Başka bir ifadeyle, geçici bir hata oluşursa her sorgu ve her SaveChangesAsync()
çağrısı bir birim olarak yeniden denenir.
Ancak, kodunuz BeginTransactionAsync()
kullanarak bir işlem başlatırsa, birim olarak ele alınması gereken kendi işlem grubunuzu tanımlıyorsunuz ve bir hata meydana gelirse işlemin içindeki her şeyin geri alınması gerekir. Yürütme stratejisini kullanırken bunu yapmaya çalışırsanız aşağıdakine benzer bir özel durum alırsınız:
InvalidOperationException: Yapılandırılmış yürütme stratejisi 'SqlServerRetryingExecutionStrategy' kullanıcı tarafından başlatılan işlemleri desteklemez. İşlemdeki tüm işlemleri yeniden denenebilir bir birim olarak yürütmek için 'DbContext.Database.CreateExecutionStrategy()' tarafından döndürülen yürütme stratejisini kullanın.
Çözüm, yürütülmesi gereken her şeyi temsil eden bir temsilciyle yürütme stratejisini el ile çağırmaktır. Geçici bir hata oluşursa yürütme stratejisi temsilciyi yeniden çağırır.
using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(
async () =>
{
using var context = new BloggingContext();
await using var transaction = await context.Database.BeginTransactionAsync();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
await context.SaveChangesAsync();
context.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
await context.SaveChangesAsync();
await transaction.CommitAsync();
});
Bu yaklaşım ortam işlemleriyle de kullanılabilir.
using var context1 = new BloggingContext();
context1.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/visualstudio" });
var strategy = context1.Database.CreateExecutionStrategy();
await strategy.ExecuteAsync(
async () =>
{
using var context2 = new BloggingContext();
using var transaction = new TransactionScope();
context2.Blogs.Add(new Blog { Url = "http://blogs.msdn.com/dotnet" });
await context2.SaveChangesAsync();
await context1.SaveChangesAsync();
transaction.Complete();
});
İşlem onayı hatası ve idempotentlik sorunu
Genel olarak, bir bağlantı hatası olduğunda geçerli işlem geri alınır. Ancak, işlem işlenirken bağlantı bırakılırsa işlemin sonuç durumu bilinmez.
Varsayılan olarak, yürütme stratejisi işlemi sanki işlem geri alınmış gibi tekrar dener; ancak durum böyle değilse ve yeni veritabanı durumu uyumsuzsa ya da veritabanının belirli bir duruma bağlı olmayan bir işlem, veri bozulmasına yol açabiliyorsa, bu bir istisnaya neden olur. Örneğin, otomatik olarak oluşturulmuş anahtar değerlerle yeni bir satır eklerken bu durum meydana gelebilir.
Bununla başa çıkmanın birkaç yolu vardır.
Seçenek 1 - Hiçbir şey yapma (neredeyse)
İşlem işleme sırasında bağlantı hatası olasılığı düşüktür, bu nedenle bu koşul gerçekten oluşursa uygulamanızın başarısız olması kabul edilebilir olabilir.
Ancak, yinelenen bir satır eklemek yerine özel durum oluşturulmasını sağlamak için depo tarafından oluşturulan anahtarları kullanmaktan kaçınmanız gerekir. İstemci tarafından oluşturulan bir GUID değeri veya istemci tarafı değer oluşturucu kullanmayı göz önünde bulundurun.
Seçenek 2 - Uygulama durumunu yeniden oluşturma
- Geçerli olan
DbContext
'ı çıkarın. - Yeni bir
DbContext
oluşturun ve uygulamanızın durumunu veritabanından geri yükleyin. - Son işlemin başarıyla tamamlanmamış olabileceğini kullanıcıya bildirin.
Seçenek 3 - Durum doğrulaması ekleme
Veritabanı durumunu değiştiren işlemlerin çoğu için başarılı olup olmadığını denetleen kod eklemek mümkündür. EF bunu kolaylaştırmak için bir uzantı yöntemi sağlar- IExecutionStrategy.ExecuteInTransaction
.
Bu yöntem bir işlemi başlatır ve işler 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.
using var db = new BloggingContext();
var strategy = db.Database.CreateExecutionStrategy();
var blogToAdd = new Blog { Url = "http://blogs.msdn.com/dotnet" };
db.Blogs.Add(blogToAdd);
await strategy.ExecuteInTransactionAsync(
db,
operation: (context, cancellationToken) => context.SaveChangesAsync(acceptAllChangesOnSuccess: false, cancellationToken),
verifySucceeded: (context, cancellationToken) => context.Blogs.AsNoTracking().AnyAsync(b => b.BlogId == blogToAdd.BlogId, cancellationToken));
db.ChangeTracker.AcceptAllChanges();
Not
Burada acceptAllChangesOnSuccess
, false
olarak ayarlanarak SaveChanges
çağrılır; bu sayede SaveChanges
başarılı olursa Blog
varlığının durumu Unchanged
olarak değiştirilmez. Bu, işleme başarısız olursa ve işlem geri alınırsa aynı işlemi yeniden denemenize olanak tanır.
Seçenek 4 - İşlemi el ile izleme
Depo tarafından oluşturulan anahtarları kullanmanız veya gerçekleştirilen işlemeye bağlı olmayan işleme hatalarını işlemek için genel bir yönteme ihtiyacınız varsa, işleme başarısız olduğunda her işlemeye denetlenen bir kimlik atanabilir.
- İşlemlerin durumunu izlemek için kullanılan veritabanına bir tablo ekleyin.
- Her işlemin başında tabloya bir satır ekleyin.
- İşleme sırasında bağlantı başarısız olursa, veritabanında ilgili satırın olup olmadığını denetleyin.
- İşleme başarılı olursa, tablonun büyümesini önlemek için ilgili satırı silin.
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);
await strategy.ExecuteInTransactionAsync(
db,
operation: (context, cancellationToken) => context.SaveChangesAsync(acceptAllChangesOnSuccess: false, cancellationToken),
verifySucceeded: (context, cancellationToken) => context.Transactions.AsNoTracking().AnyAsync(t => t.Id == transaction.Id, cancellationToken));
db.ChangeTracker.AcceptAllChanges();
db.Transactions.Remove(transaction);
await db.SaveChangesAsync();
Dikkat
Doğrulama için kullanılan bağlamın, işlem işleme sırasında başarısız olması durumunda bağlantının doğrulama sırasında yeniden başarısız olma olasılığı yüksek olarak tanımlanmış bir yürütme stratejisine sahip olduğundan emin olun.
Ek kaynaklar
- Azure SQL Veritabanı ve SQL Yönetilen Örneği geçici bağlantı hatalarını giderme