Elastyczność połączeniaConnection Resiliency

Elastyczność połączenia automatycznie ponawia próbę polecenia bazy danych nie powiodło się.Connection resiliency automatically retries failed database commands. Funkcja może być używana z żadną bazą danych, podając "strategii wykonywania", która hermetyzuje logikę niezbędną do wykrywania błędów i ponów próbę wykonania polecenia.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 dostawców można podać strategii wykonywania dopasowane do swoich warunków błędów konkretnej bazy danych i zasady ponawiania optymalne.EF Core providers can supply execution strategies tailored to their specific database failure conditions and optimal retry policies.

Na przykład dostawca programu SQL Server zawiera strategii wykonywania, który jest w szczególny sposób dopasowane do programu SQL Server (w tym usługi SQL Azure).As an example, the SQL Server provider includes an execution strategy that is specifically tailored to SQL Server (including SQL Azure). Ją rozpoznaje rodzaje wyjątków, które mogą być ponawiane i posiada odpowiednie ustawienia domyślne, aby uzyskać maksymalną liczbę ponownych prób, opóźnienie między ponownych prób.It is aware of the exception types that can be retried and has sensible defaults for maximum retries, delay between retries, etc.

Strategia wykonywania jest określony podczas konfigurowania opcji dla kontekstu.An execution strategy is specified when configuring the options for your context. Jest to zazwyczaj w OnConfiguring metody pochodnej kontekstu lub w Startup.cs dla aplikacji platformy ASP.NET Core.This is typically in the OnConfiguring method of your derived context, or in Startup.cs for an ASP.NET Core application.

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

Strategia wykonywania niestandardowychCustom execution strategy

Istnieje mechanizm do zarejestrowania strategii wykonywania niestandardowych samodzielnie, jeśli chcesz zmienić dowolne z ustawień domyślnych.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(...));
}

Strategii wykonywania i transakcjiExecution strategies and transactions

Strategii wykonywania, który automatycznie ponawia próbę na błędy musi być w stanie odtworzyć każdej operacji w bloku ponawiania, która kończy się niepowodzeniem.An execution strategy that automatically retries on failures needs to be able to play back each operation in a retry block that fails. Po włączeniu ponownych prób każdej operacji wykonywanych przy użyciu programu EF Core staje się własną wywołały operacji.When retries are enabled, each operation you perform via EF Core becomes its own retriable operation. Oznacza to, każde zapytanie i każde wywołanie SaveChanges() zostanie ponowiona jako jednostki, jeśli wystąpi błąd przejściowy.That is, each query and each call to SaveChanges() will be retried as a unit if a transient failure occurs.

Jednakże jeżeli Twój kod inicjuje transakcji przy użyciu BeginTransaction() definiujesz własną grupę działań, które muszą być traktowane jako jednostka i wszystko wewnątrz transakcji musi być odtwarzane ma miejsce awaria.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. Jeśli spróbuje to zrobić, korzystając z strategii wykonywania, zostanie wyświetlony następujący wyjątek:You will receive an exception like the following if you attempt to do this when using an execution strategy:

InvalidOperationException: Strategia wykonywania skonfigurowany "SqlServerRetryingExecutionStrategy" nie obsługuje transakcji zainicjowanej przez użytkownika.InvalidOperationException: The configured execution strategy 'SqlServerRetryingExecutionStrategy' does not support user initiated transactions. Strategia wykonywania zwróconych przez "DbContext.Database.CreateExecutionStrategy()" umożliwia wykonywanie wszystkich operacji w transakcji jako jednostka z możliwością ponowienia próby.Use the execution strategy returned by 'DbContext.Database.CreateExecutionStrategy()' to execute all the operations in the transaction as a retriable unit.

To rozwiązanie jest ręcznie wywołać strategii wykonywania z delegatem reprezentujący wszystko, co ma zostać wykonana.The solution is to manually invoke the execution strategy with a delegate representing everything that needs to be executed. Jeśli wystąpi błąd przejściowy, strategia wykonywania wywoła delegata ponownie.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();
            }
        }
    });
}

Błąd zatwierdzania transakcji i problem idempotentnościTransaction commit failure and the idempotency issue

Ogólnie rzecz biorąc gdy wystąpi awaria połączenia bieżąca transakcja zostanie wycofana.In general, when there is a connection failure the current transaction is rolled back. Jednakże, jeśli połączenie zostało przerwane, gdy transakcja jest zwrócenia zatwierdzone Wynikowy stan transakcji jest nieznany.However, if the connection is dropped while the transaction is being committed the resulting state of the transaction is unknown. Zobacz ten wpis w blogu Aby uzyskać więcej informacji.See this blog post for more details.

Domyślnie strategii wykonywania ponowi operację tak, jakby transakcja została wycofana, ale jeśli nie jest to wynikiem będzie wyjątek nowy stan bazy danych jest niezgodny lub może prowadzić do uszkodzenie danych Jeśli Operacja nie zależą od określonego stanu, na przykład podczas wstawiania nowego wiersza przy użyciu automatycznego generowania wartości klucza.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.

Istnieje kilka sposobów, aby poradzić sobie z tym.There are several ways to deal with this.

Opcja 1 — czy (prawie) nothingOption 1 - Do (almost) nothing

Prawdopodobieństwo awarii połączenia podczas zatwierdzania transakcji jest niska, dlatego może być akceptowalne, aby aplikacja została właśnie się niepowodzeniem, jeśli rzeczywiście występuje ten problem.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.

Jednak należy unikać używania generowane przez magazyn kluczy w celu zapewnienia, że zamiast opcji dodawania zduplikowany wiersz jest zgłaszany wyjątek.However, you need to avoid using store-generated keys in order to ensure that an exception is thrown instead of adding a duplicate row. Należy rozważyć użycie wartość identyfikatora GUID generowany przez klienta lub generator wartości po stronie klienta.Consider using a client-generated GUID value or a client-side value generator.

Opcja 2 — stan aplikacji ponownej kompilacjiOption 2 - Rebuild application state

  1. Odrzucenie aktualnego DbContext.Discard the current DbContext.
  2. Utwórz nową DbContext i przywrócenia stanu aplikacji z bazy danych.Create a new DbContext and restore the state of your application from the database.
  3. Informuje użytkownika, że ostatnia operacja może nie zostały zakończone pomyślnie.Inform the user that the last operation might not have been completed successfully.

Opcja 3 — Dodawanie weryfikacji do stanuOption 3 - Add state verification

Dla większości działań, które zmieniają stan bazy danych jest można dodać kod, który sprawdza, czy powiodła się.For most of the operations that change the database state it is possible to add code that checks whether it succeeded. EF udostępnia metodę rozszerzenia, aby to ułatwić - IExecutionStrategy.ExecuteInTransaction.EF provides an extension method to make this easier - IExecutionStrategy.ExecuteInTransaction.

Ta metoda rozpoczyna się i zatwierdzeń transakcji i akceptuje także funkcję verifySucceeded parametrów, które jest wywoływane, gdy wystąpi błąd przejściowy podczas zatwierdzania transakcji.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();
}

Uwaga

W tym miejscu SaveChanges jest wywoływana z acceptAllChangesOnSuccess równa false Aby uniknąć zmieniania stanu Blog jednostki do Unchanged Jeśli SaveChanges zakończy się pomyślnie.Here SaveChanges is invoked with acceptAllChangesOnSuccess set to false to avoid changing the state of the Blog entity to Unchanged if SaveChanges succeeds. Dzięki temu, aby ponowić próbę wykonania tej samej operacji, jeśli zatwierdzenie zakończy się niepowodzeniem, a transakcja zostanie wycofana.This allows to retry the same operation if the commit fails and the transaction is rolled back.

Opcja 4 — ręcznie śledzić transakcjiOption 4 - Manually track the transaction

Jeśli musisz używać generowane przez magazyn kluczy lub potrzebujesz ogólny sposób obsługi błędów zatwierdzania, który nie są zależne od operacji wykonywanych każdej transakcji można przypisać Identyfikatora, który jest sprawdzany, jeśli zatwierdzenie zakończy się niepowodzeniem.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. Dodaj tabelę w bazie danych używane do śledzenia stanu transakcji.Add a table to the database used to track the status of the transactions.
  2. Wstaw wiersz do tabeli na początku każdej transakcji.Insert a row into the table at the beginning of each transaction.
  3. Jeśli połączenie nie powiedzie się podczas zatwierdzania, sprawdź, czy obecność odpowiedni wiersz w bazie danych.If the connection fails during the commit, check for the presence of the corresponding row in the database.
  4. Jeśli zatwierdzenie zakończy się pomyślnie, usuń odpowiedni wiersz w celu uniknięcia wzrostu tabeli.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();
}

Uwaga

Upewnij się, że kontekst użyty w celu weryfikacji ma zdefiniowany jako połączenie jest prawdopodobnie nastąpi ich awaria ponownie podczas weryfikacji w przypadku niepowodzenia podczas zatwierdzania transakcji strategii wykonywania.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.