VerbindungsresilienzConnection Resiliency

Verbindungsresilienz versucht automatisch fehlerhaften Datenbankbefehle auszuführen.Connection resiliency automatically retries failed database commands. Die Funktion kann mit einer beliebigen Datenbank verwendet werden, um durch Angabe einer "Ausführungsstrategie", die erforderliche Logik zum Erkennen von Fehlern aus, und wiederholen die Befehle kapselt.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-Anbieter können Ausführungsstrategien maßgeschneidert für ihre Datenbank fehlerbedingungen und optimale wiederholungsrichtlinien angeben.EF Core providers can supply execution strategies tailored to their specific database failure conditions and optimal retry policies.

Beispielsweise enthält den SQL Server-Anbieter eine Ausführungsstrategie, die speziell auf SQL Server (einschließlich SQL Azure) zugeschnitten ist.As an example, the SQL Server provider includes an execution strategy that is specifically tailored to SQL Server (including SQL Azure). Es erkennt die Ausnahmetypen, die wiederholt werden können und verfügt über sinnvolle Standardwerte für maximale Wiederholungsversuche, Verzögerung zwischen Wiederholungen usw.It is aware of the exception types that can be retried and has sensible defaults for maximum retries, delay between retries, etc.

Eine Ausführungsstrategie für die wird angegeben, wenn Sie die Optionen für den Kontext zu konfigurieren.An execution strategy is specified when configuring the options for your context. Dies ist in der Regel in der OnConfiguring Methode, die von Ihrem abgeleiteten Kontext oder Startup.cs für eine ASP.NET Core-Anwendung.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());
}

Benutzerdefinierte AusführungsstrategieCustom execution strategy

Es gibt ein Mechanismus zum Registrieren einer benutzerdefinierten Ausführungsstrategie selbst, wenn Sie die Standardwerte ändern möchten.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(...));
}

Ausführungsstrategien und TransaktionenExecution strategies and transactions

Eine Ausführungsstrategie, die automatisch bei Fehlern versucht werden soll, muss in der Lage, jeden Vorgang in einen wiederholungsversuchblock wiederzugeben, die nicht.An execution strategy that automatically retries on failures needs to be able to play back each operation in a retry block that fails. Wenn Wiederholungen aktiviert sind, wird jeder Vorgang, die Sie über EF Core ausführen individuell wiederholbaren Vorgang.When retries are enabled, each operation you perform via EF Core becomes its own retriable operation. Das heißt, jede Abfrage und jeder Aufruf von SaveChanges() wird als eine Einheit wiederholt, wenn ein vorübergehender Fehler auftritt.That is, each query and each call to SaveChanges() will be retried as a unit if a transient failure occurs.

Aber wenn Ihr Code initiiert eine Transaktion mit BeginTransaction() definieren Sie Ihre eigene Gruppe von Vorgängen, die als Einheit behandelt werden müssen, und alles innerhalb der Transaktion wiedergegeben werden soll ein Fehler auftreten muss.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. Sie erhalten eine Ausnahme wie folgt, wenn Sie versuchen, dies zu tun, wenn Sie eine Ausführungsstrategie verwenden:You will receive an exception like the following if you attempt to do this when using an execution strategy:

"InvalidOperationException": Die konfigurierte Ausführungsstrategie "SqlServerRetryingExecutionStrategy" unterstützt keine vom Benutzer initiierte Transaktionen.InvalidOperationException: The configured execution strategy 'SqlServerRetryingExecutionStrategy' does not support user initiated transactions. Verwenden Sie die Ausführungsstrategie, die von „DbContext.Database.CreateExecutionStrategy()“ zurückgegeben wird, um alle Vorgänge in der Transaktion als wiederholbare Einheit auszuführen.Use the execution strategy returned by 'DbContext.Database.CreateExecutionStrategy()' to execute all the operations in the transaction as a retriable unit.

Die Lösung besteht darin, manuell die Ausführungsstrategie mit einem Delegaten, der alle Komponenten darstellt aufrufen, die ausgeführt werden muss.The solution is to manually invoke the execution strategy with a delegate representing everything that needs to be executed. Die Ausführungsstrategie ruft den Delegaten erneut auf, wenn ein vorübergehender Fehler auftritt.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();
            }
        }
    });
}

Fehler beim Commit der Transaktion und das Problem IdempotenzTransaction commit failure and the idempotency issue

Im Allgemeinen wird bei ein Verbindungsfehler die aktuelle Transaktion ein Rollback ausgeführt.In general, when there is a connection failure the current transaction is rolled back. Allerdings, wenn die Verbindung getrennt wird, während die Transaktion wird ein Commit der resultierende Status der Transaktion ist unbekannt.However, if the connection is dropped while the transaction is being committed the resulting state of the transaction is unknown. Finden Sie in diesem Blogbeitrag Weitere Details.See this blog post for more details.

In der Standardeinstellung die Ausführungsstrategie wiederholt den Vorgang wurde ein Rollback für die Transaktion, wobei jedoch ist dies nicht der Fall dies zu einer Ausnahme führt, wenn der neue Datenbankstatus nicht kompatibel ist oder zu führen datenbeschädigung Wenn die Vorgang beruht nicht auf einem bestimmten Status, z. B. wenn eine neue Zeile mit automatisch generierter Schlüsselwerte einfügen.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.

Es gibt mehrere Möglichkeiten, diese verarbeiten.There are several ways to deal with this.

Option 1: nothing (fast)Option 1 - Do (almost) nothing

Die Wahrscheinlichkeit für einen Verbindungsfehler während des Transaktionscommits ist gering, sodass es für die Anwendung nur fehlschlägt, wenn diese Bedingung, tatsächlich eintritt gültig sein kann.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.

Allerdings müssen Sie zu vermeiden, vom Speicher generierte Schlüssel verwenden, um sicherzustellen, dass eine Ausnahme ausgelöst wird, anstatt eine Zeile mit doppelte hinzuzufügen.However, you need to avoid using store-generated keys in order to ensure that an exception is thrown instead of adding a duplicate row. Sollten Sie einen Client generierter GUID-Wert oder einen für die clientseitige-wertgenerator.Consider using a client-generated GUID value or a client-side value generator.

Option 2 – Rebuild-AnwendungsstatusOption 2 - Rebuild application state

  1. Verwerfen der aktuellen DbContext.Discard the current DbContext.
  2. Erstellen Sie ein neues DbContext und Wiederherstellen des Zustands Ihrer Anwendung aus der Datenbank.Create a new DbContext and restore the state of your application from the database.
  3. Informieren Sie den Benutzer, dass der letzte Vorgang nicht erfolgreich abgeschlossen wurde haben kann.Inform the user that the last operation might not have been completed successfully.

Option 3 - Status-Überprüfung hinzufügenOption 3 - Add state verification

Für den Großteil der Vorgänge, die den Zustand der Datenbank zu ändern, ist es möglich, Code hinzuzufügen, die überprüft, ob es erfolgreich war.For most of the operations that change the database state it is possible to add code that checks whether it succeeded. EF bietet eine Erweiterungsmethode zur - Vereinfachung IExecutionStrategy.ExecuteInTransaction.EF provides an extension method to make this easier - IExecutionStrategy.ExecuteInTransaction.

Diese Methode beginnt und führt einen Commit für eine Transaktion und akzeptiert auch eine Funktion in der verifySucceeded Parameter, der aufgerufen wird, wenn ein vorübergehender Fehler, während das Commit der Transaktion auftritt.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();
}

Hinweis

Hier SaveChanges wird aufgerufen, mit acceptAllChangesOnSuccess festgelegt false um zu vermeiden, Ändern des Status der Blog Entität Unchanged Wenn SaveChanges erfolgreich ausgeführt wird.Here SaveChanges is invoked with acceptAllChangesOnSuccess set to false to avoid changing the state of the Blog entity to Unchanged if SaveChanges succeeds. Dadurch können um den gleichen Vorgang zu wiederholen, wenn der Commit schlägt fehl, und die Transaktion ein Rollback.This allows to retry the same operation if the commit fails and the transaction is rolled back.

Option 4: die Transaktion manuell nachverfolgenOption 4 - Manually track the transaction

Wenn Sie müssen Speicher generierte Schlüssel oder eine generische Möglichkeit Commit Fehler zu beheben, die nicht von den ausgeführten Vorgang abhängig ist konnte jeder Transaktion eine ID zugewiesen werden, die überprüft wird, wenn der Commit schlägt fehl.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. Fügen Sie eine Tabelle mit der Datenbank verwendet, um den Status der Transaktionen nachzuverfolgen.Add a table to the database used to track the status of the transactions.
  2. Eine Zeile in der Tabelle am Anfang jeder Transaktion eingefügt.Insert a row into the table at the beginning of each transaction.
  3. Wenn die Verbindung während des Commits fehlschlägt, überprüfen Sie das Vorhandensein der entsprechenden Zeile in der Datenbank.If the connection fails during the commit, check for the presence of the corresponding row in the database.
  4. Wenn der Commit erfolgreich ausgeführt wird, löschen Sie die entsprechende Zeile aus, um die Vergrößerung der Tabelle zu vermeiden.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();
}

Hinweis

Stellen Sie sicher, dass der Kontext für die Überprüfung verwendet, eine Ausführungsstrategie definiert wurde, wie die Verbindung vermutlich erneut bei der Überprüfung fehlschlägt ist, wenn während des Transaktionscommits aufgetreten ist.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.