Odolnost připojení

Odolnost připojení automaticky znovu spustí neúspěšné databázové příkazy. Tuto funkci je možné použít s libovolnou databází poskytnutím "strategie spouštění", která zapouzdřuje logiku potřebnou k detekci selhání a příkazů opakování. EF Core poskytovatelé mohou poskytnout strategie spouštění přizpůsobené jejich konkrétním podmínkám selhání databáze a optimálním zásadám opakování.

Například poskytovatel služeb SQL Server strategii spouštění, která je specificky přizpůsobená SQL Server (včetně SQL Azure). Je si vědom typů výjimek, které je možné zopakovat, a má rozumné výchozí hodnoty pro maximální počet opakování, zpoždění mezi opakováními atd.

Strategie spouštění se zadá při konfiguraci možností pro váš kontext. To je obvykle v metodě 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 pro ASP.NET Core aplikaci:

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í, že EF interně do vyrovnávací paměti sady výsledků, 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 spouštění

Pokud chcete změnit kterékoli z výchozích hodnot, existuje mechanismus, jak vlastní strategii spouštění zaregistrovat.

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

Strategie provádění a transakce

Strategie spouštění, která při selhání automaticky zopakuje pokusy, musí být schopná přehrát každou operaci v bloku opakování, který selže. Pokud jsou opakování povolená, každá operace, kterou provedete prostřednictvím EF Core se stane vlastní operaci, kterou lze znovu provést. To znamená, že pokud dojde k přechodnému SaveChanges() selhání, každý dotaz a každé volání se zopakuje jako jednotka.

Pokud však váš kód iniciuje transakci pomocí definujete vlastní skupinu operací, BeginTransaction() které je třeba považovat za jednotku, a vše, co uvnitř transakce by bylo nutné přehrát v případě selhání. Pokud se o to pokusíte 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()k provedení všech operací v transakci jako jednotku, kterou je možné znovu spustit.

Řešením je ručně vyvolat strategii provádění s delegátem, který představuje 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 okolí transakce.


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 idempotence

Obecně platí, že pokud dojde k selhání připojení, aktuální transakce se vrátí zpět. Pokud je však připojení zrušeno, zatímco transakce je potvrzena výsledný stav transakce je neznámý.

Ve výchozím nastavení strategie provádění zkusí operaci opakovat, jako by byla transakce vrácena zpět, ale pokud tomu tak není, výsledkem bude výjimka, 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 se s tím vypořádat.

Možnost 1 – Do (téměř) nothing

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 selhala pouze v případě, že tato podmínka skutečně nastane.

Musíte se ale vyhnout použití klíčů generovaných úložištěm, abyste zajistili, že místo přidání duplicitního řádku bude vyvolána výjimka. Zvažte použití hodnoty GUID vygenerované klientem nebo generátoru hodnot na straně klienta.

Možnost 2 – Opětovné sestavení stavu aplikace

  1. Aktuální hodnotu zahoďte DbContext.
  2. Vytvořte nový a DbContext obnovte stav aplikace z databáze.
  3. 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á to usnadňuje – IExecutionStrategy.ExecuteInTransaction.

Tato metoda začíná a potvrdí transakci a verifySucceeded také přijme funkci v 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

Tady SaveChanges se vyvolá s nastavenou acceptAllChangesOnSuccess na , false aby se zabránilo změně Blog stavu entity na v Unchanged případě SaveChanges úspěchu. To umožňuje opakovat stejnou operaci, pokud potvrzení selže a transakce je vrácena 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 provedené operaci, může být každé transakci přiřazeno ID, které se kontroluje, když se potvrzení nezdaří.

  1. Přidejte tabulku do databáze, která se používá ke sledování stavu transakcí.
  2. Vložte řádek do tabulky na začátku každé transakce.
  3. Pokud připojení selže během potvrzení, zkontrolujte přítomnost odpovídajícího řádku v databázi.
  4. Pokud je potvrzení úspěšné, odstraňte odpovídající řádek, abyste se vyhnuli ná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ý pro ověření má definovanou strategii spuštění, protože pokud během potvrzení transakce selže, připojení pravděpodobně znovu selže.

Další materiály