Odolnost připojení
Odolnost připojení automaticky opakuje neúspěšné databázové příkazy. Funkci lze použít s libovolnou databází tím, ž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 podmínkám selhání databáze a optimálním zásadám opakování.
Poskytovatel SQL Server například obsahuje strategii provádění, která je speciálně přizpůsobená SQL Server (včetně SQL Azure). Je si vědom typů výjimek, které lze opakovat, a má rozumné výchozí hodnoty pro maximální opakování, zpoždění mezi opakováními atd.
Strategie provádění se zadává při konfiguraci možností pro váš kontext. 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",
options => options.EnableRetryOnFailure());
}
nebo v Startup.cs
případě ASP.NET Core aplikace:
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í .
Vlastní strategie provádě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 selhání, musí být schopna přehrát každou operaci v bloku opakování, který selže. Po povolení opakování se každá operace, kterou provádíte prostřednictvím EF Core, stane svou 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 inicializuje transakci pomocí BeginTransaction()
definování vlastní skupiny operací, které je potř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. Pomocí strategie provádění vrácené dbContext.Database.CreateExecutionStrategy() proveďte všechny operace v transakci jako opakovatelnou jednotku.
Řešením je ručně vyvolat strategii provádění pomocí delegáta představujícího 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 použít také 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 se však připojení zahodí během potvrzení transakce, 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ěkolikzpůsobůch
Možnost 1 – Nedělejte (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.
Pokud ale chcete zajistit, aby se místo přidání duplicitního řádku vyvolala výjimka, musíte se vyhnout použití klíčů generovaný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
- Zahoďte aktuální
DbContext
. - Vytvořte nový
DbContext
a obnovte stav aplikace 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ý kontroluje, jestli byl úspěšný. EF poskytuje metodu rozšíření, 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, když dojde k 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 acceptAllChangesOnSuccess
sada, aby false
se zabránilo změně stavu Blog
entity na, pokud Unchanged
je SaveChanges
to úspěšné. 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 klíče generované úložištěm 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é se kontroluje, když potvrzení selže.
- 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 zabránili 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á strategii provádění definovanou jako připojení pravděpodobně selhává při ověření, pokud během potvrzení transakce selhala.