Odolnost připojení
Připojení odolnost proti chybám automaticky opakuje neúspěšné databázové příkazy. Tuto funkci lze použít s libovolnou databází tak, že zadáte "strategii provádění", která zapouzdřuje logiku potřebnou k detekci selhání a opakování příkazů. Poskytovatelé EF Core můžou poskytovat strategie provádění přizpůsobené konkrétním stavům selhání databáze a optimálním zásadám opakování.
Poskytovatel SQL Serveru například obsahuje strategii spouštění, která je speciálně přizpůsobená SQL Serveru (včetně SQL Azure). Je si vědom typů výjimek, které je možné opakovat, a má rozumné výchozí hodnoty pro maximální opakování, zpoždění mezi opakováními atd.
Při konfiguraci možností pro váš kontext se zadává strategie provádění. Obvykle se jedná o metodu OnConfiguring
odvozeného kontextu:
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseSqlServer(
@"Server=(localdb)\mssqllocaldb;Database=EFMiscellanous.ConnectionResiliency;Trusted_Connection=True;ConnectRetryCount=0",
options => options.EnableRetryOnFailure());
}
nebo v Startup.cs
případě aplikace ASP.NET Core:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<PicnicContext>(
options => options.UseSqlServer(
"<connection string>",
providerOptions => providerOptions.EnableRetryOnFailure()));
}
Poznámka
Povolení opakování při selhání způsobí interní ukládání sady výsledků ef do vyrovnávací paměti, což může výrazně zvýšit požadavky na paměť pro dotazy vracející velké sady výsledků. Další podrobnosti najdete v tématu ukládání do vyrovnávací paměti a streamování .
Strategie vlastního spuštění
Existuje mechanismus pro registraci vlastní strategie spouštění, pokud chcete změnit některou z výchozích hodnot.
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
optionsBuilder
.UseMyProvider(
"<connection string>",
options => options.ExecutionStrategy(...));
}
Strategie provádění a transakce
Strategie provádění, která automaticky opakuje pokusy o selhání, musí být schopná přehrát každou operaci v bloku opakování, který selže. Pokud jsou povolené opakování, každá operace, kterou provedete přes EF Core, se stane vlastní retriable operací. To znamená, že každý dotaz a každé volání SaveChanges()
se bude opakovat jako jednotka, pokud dojde k přechodnému selhání.
Pokud však váš kód zahájí transakci pomocí BeginTransaction()
definování vlastní skupiny operací, které je třeba považovat za jednotku, a všechno uvnitř transakce by se mělo přehrát, dojde k selhání. Pokud se to pokusíte provést při použití strategie provádění, zobrazí se výjimka podobná následující:
InvalidOperationException: Nakonfigurovaná strategie spouštění SqlServerRetryingExecutionStrategy nepodporuje transakce iniciované uživatelem. Použijte strategii provádění vrácenou dbContext.Database.CreateExecutionStrategy() ke spuštění všech operací v transakci jako opakovatelné jednotky.
Řešením je ručně vyvolat strategii provádění s delegátem představujícím vše, co je potřeba provést. Pokud dojde k přechodnému selhání, strategie provádění znovu vyvolá delegáta.
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();
});
Tento přístup lze také použít s okolními transakcemi.
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();
});
Selhání potvrzení transakce a problém s idempotenci
Obecně platí, že pokud dojde k selhání připojení, aktuální transakce se vrátí zpět. Pokud je však připojení ukončeno, zatímco transakce je potvrzena výsledný stav transakce je neznámý.
Ve výchozím nastavení strategie provádění operaci zopakuje, jako kdyby byla transakce vrácena zpět, ale pokud tomu tak není, dojde k výjimce, pokud je nový stav databáze nekompatibilní nebo může vést k poškození dat, pokud operace nespoléhá na konkrétní stav, například při vložení nového řádku s automaticky generovanými hodnotami klíče.
Existuje několik způsobů, jak to vyřešit.
Možnost 1 – Dělat (téměř) nic
Pravděpodobnost selhání připojení během potvrzení transakce je nízká, takže může být přijatelné, aby vaše aplikace mohla pouze selhat, pokud k této podmínce skutečně dojde.
Abyste ale zajistili, že místo přidání duplicitního řádku dojde k výjimce, musíte se vyhnout použití klíčů vygenerovaných úložištěm. Zvažte použití hodnoty GUID generovaného klientem nebo generátoru hodnot na straně klienta.
Možnost 2 – Opětovné sestavení stavu aplikace
- Zahodit aktuální
DbContext
. - Vytvořte nový
DbContext
stav aplikace a obnovte ho z databáze. - Informujte uživatele, že poslední operace pravděpodobně nebyla úspěšně dokončena.
Možnost 3 – Přidání ověření stavu
U většiny operací, které mění stav databáze, je možné přidat kód, který zkontroluje, jestli byl úspěšný. EF poskytuje rozšiřující metodu, která usnadňuje - IExecutionStrategy.ExecuteInTransaction
.
Tato metoda začíná a potvrdí transakci a také přijímá funkci v verifySucceeded
parametru, který je vyvolán při přechodné chybě během potvrzení transakce.
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();
Poznámka
Zde SaveChanges
je vyvolána sada, acceptAllChangesOnSuccess
aby false
se zabránilo změně stavu Blog
entity na Unchanged
úspěšné SaveChanges
. To umožňuje opakovat stejnou operaci, pokud potvrzení selže a transakce se vrátí zpět.
Možnost 4 – Ruční sledování transakce
Pokud potřebujete použít uložené klíče nebo potřebujete obecný způsob zpracování selhání potvrzení, který nezávisí na operaci prováděné každou transakcí, může být přiřazeno ID, které je kontrolováno při selhání potvrzení.
- Přidejte do databáze tabulku použitou ke sledování stavu transakcí.
- Vložte řádek do tabulky na začátku každé transakce.
- Pokud se připojení během potvrzení nezdaří, zkontrolujte přítomnost odpovídajícího řádku v databázi.
- Pokud je potvrzení úspěšné, odstraňte odpovídající řádek, abyste se vyhnuli růstu tabulky.
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();
Poznámka
Ujistěte se, že kontext použitý k ověření má definovanou strategii provádění, protože připojení pravděpodobně při ověření selže, pokud během potvrzení transakce selhalo.