VerbindungsstabilitätConnection Resiliency

Verbindungsresilienz führt automatisch Wiederholungsversuche für fehlerhafte Datenbankbefehle.Connection resiliency automatically retries failed database commands. Die Funktion kann mit einer beliebigen Datenbank verwendet werden, durch Angabe einer "Ausführungsstrategie", die die Logik zum Erkennen von Fehlern, 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 auf ihre Datenbank fehlerbedingungen und optimale wiederholungsrichtlinien zugeschnittene Ausführungsstrategien angeben.EF Core providers can supply execution strategies tailored to their specific database failure conditions and optimal retry policies.

Beispielsweise enthält die 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). Er erkennt die Ausnahmetypen, die wiederholt werden können und sensibler Standardwerte für die maximal Anzahl von Wiederholungen, Verzögerung zwischen Wiederholungen usw. enthält.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 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 von den 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 ist ein Mechanismus zum Registrieren einer benutzerdefinierten Ausführungsstrategie Ihrer Wahl, 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 auf Fehler 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 an retry block that fails. Wenn Wiederholungen aktiviert sind, wird jeder Vorgang, die Sie über EF Core ausführen eigener wiederholbar Vorgang, d. h. jede Abfrage und jeder Aufruf von SaveChanges() wird als eine Einheit wiederholt, wenn ein vorübergehender Fehler auftritt.When retries are enabled, each operation you perform via EF Core becomes its own retriable operation, i.e. each query and each call to SaveChanges() will be retried as a unit if a transient failure occurs.

Jedoch wenn Codes initiiert eine Transaktion mit BeginTransaction() definieren Sie eine eigene Gruppe von Vorgängen, die als Einheit behandelt werden müssen, d. h. müssen alles innerhalb der Transaktion wiedergegeben werden soll ein Fehler auftreten.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, i.e. everything inside the transaction would need to be played back shall a failure occur. Sie erhalten eine der folgenden vergleichbare Ausnahme, wenn Sie versuchen, dieses Verfahren, wenn 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 initiierten Transaktionen.InvalidOperationException: The configured execution strategy 'SqlServerRetryingExecutionStrategy' does not support user initiated transactions. Verwenden Sie die Ausführungsstrategie "DbContext.Database.CreateExecutionStrategy()" zurückgegebene, um alle Vorgänge in der Transaktion als Einheit mit möglichem 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 aufrufen Ausführungsstrategie mit einen Delegaten, die alle Elemente darstellt, die ausgeführt werden muss.The solution is to manually invoke the execution strategy with a delegate representing everything that needs to be executed. Um ein vorübergehender Fehler auftritt, wird die Ausführungsstrategie den Delegaten erneut aufrufen.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, wenn ein Verbindungsfehler vorhanden ist die aktuelle Transaktion ein Rollback ausgeführt.In general, when there is a connection failure the current transaction is rolled back. Jedoch wenn die Verbindung getrennt wird, während die Transaktion wird 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. Lesen Sie diese Blogbeitrag Weitere Details.See this blog post for more details.

Wird standardmäßig die Ausführungsstrategie wiederholt den Vorgang als ob Rollback der Transaktion, ist dies nicht der Fall Dies führt jedoch eine Ausnahme ausgelöst, wenn der neuen Datenbankstatus nicht kompatibel ist oder zur führen datenbeschädigung Wenn die Vorgang beruht nicht auf einem bestimmten Status, z. B. Wenn Sie eine neue Zeile mit automatisch generierten 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, zu diesem Zweck verfügt.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 sie möglicherweise für Ihre Anwendung nur fehlschlägt, wenn tatsächlich diese Bedingung tritt ein akzeptabel.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 zur Vermeidung des Speicher generierte Schlüssel, um sicherzustellen, dass eine Ausnahme ausgelöst wird, anstatt eine doppelte Zeile 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. Erwägen Sie eine Client-generierte 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 den aktuellen DbContext.Discard the current DbContext.
  2. Erstellen Sie ein neues DbContext und Wiederherstellen des Zustands der Anwendung aus der Datenbank.Create a new DbContext and restore the state of your application from the database.
  3. Dem Benutzer zu informieren, dass der letzte Vorgang nicht erfolgreich abgeschlossen wurden wurden möglicherweise.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 die meisten Vorgänge, die den Status einer Datenbank ändern, ist es möglich, Code hinzufügen, der überprüft, ob es erfolgreich ausgeführt wurde.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 startet einen Commit für eine Transaktion und akzeptiert auch eine Funktion in der verifySucceeded Parameter, der aufgerufen wird, wenn ein vorübergehender Fehler beim Commit 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 mit aufgerufen wird acceptAllChangesOnSuccess festgelegt false 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. Dies ermöglicht um den gleichen Vorgang zu wiederholen, wenn das 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 verwenden, oder benötigen eine generische Methode der Behandlung von commitfehlern, die nicht auf den ausgeführten Vorgang abhängig konnte jede Transaktion eine ID zugewiesen, die überprüft wird, wenn dies fehlschlägt.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. In der Tabelle am Anfang jeder Transaktion eine Zeile eingefügt werden.Insert a row into the table at the beginning of each transaction.
  3. Wenn die Verbindung während der Commit-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 das Commit erfolgreich ausgeführt wurde, 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, wie die Verbindung wahrscheinlich erneut bei der Überprüfung fehl 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.