Behandlung von ParallelitätskonfliktenHandling Concurrency Conflicts

Hinweis

Auf dieser Seite wird erläutert, wie Parallelität in EF Core funktioniert und wie sich Nebenläufigkeitskonflikte in Ihrer Anwendung handhaben lassen.This page documents how concurrency works in EF Core and how to handle concurrency conflicts in your application. Weitere Informationen zum Konfigurieren von Parallelitätstoken in Ihrem Modell finden Sie unter Parallelitätstoken.See Concurrency Tokens for details on how to configure concurrency tokens in your model.

Tipp

Das in diesem Artikel verwendete Beispiel finden Sie auf GitHub.You can view this article's sample on GitHub.

Datenbankparallelität ist gegeben, wenn mehrere Prozesse oder Benutzer gleichzeitig auf dieselben Daten in einer Datenbank zugreifen oder diese ändern.Database concurrency refers to situations in which multiple processes or users access or change the same data in a database at the same time. Mit Parallelitätssteuerung sind bestimmte Mechanismen gemeint, mit denen die Datenkonsistenz bei gleichzeitigen Änderungen sichergestellt wird.Concurrency control refers to specific mechanisms used to ensure data consistency in presence of concurrent changes.

EF Core implementiert eine optimistische Parallelitätssteuerung: Mehrere Prozesse oder Benutzer können also unabhängig voneinander Änderungen vornehmen, ohne dass der Mehraufwand einer Synchronisierung oder Sperrung anfällt.EF Core implements optimistic concurrency control, meaning that it will let multiple processes or users make changes independently without the overhead of synchronization or locking. Im Idealfall wirken sich diese Änderungen nicht aufeinander aus und werden daher übernommen.In the ideal situation, these changes will not interfere with each other and therefore will be able to succeed. Im schlechtesten Fall versuchen zwei oder mehr Prozesse, in Konflikt stehende Änderungen vorzunehmen, und nur einer davon ist erfolgreich.In the worst case scenario, two or more processes will attempt to make conflicting changes, and only one of them should succeed.

Funktionsweise der Parallelitätssteuerung in EF CoreHow concurrency control works in EF Core

Mithilfe von Eigenschaften, die als Parallelitätstoken konfiguriert werden, wird die optimistische Parallelitätssteuerung implementiert: Immer wenn während SaveChanges ein Update- oder Löschvorgang ausgeführt wird, wird der Wert des Parallelitätstokens der Datenbank mit dem ursprünglichen Wert verglichen, der von EF Core gelesen wird.Properties configured as concurrency tokens are used to implement optimistic concurrency control: whenever an update or delete operation is performed during SaveChanges, the value of the concurrency token on the database is compared against the original value read by EF Core.

  • Falls die Werte übereinstimmen, kann der Vorgang abgeschlossen werden.If the values match, the operation can complete.
  • Ist dies jedoch nicht der Fall, geht EF Core davon aus, dass ein anderer Benutzer einen in Konflikt stehenden Vorgang ausgeführt hat, und bricht die aktuelle Transaktion ab.If the values do not match, EF Core assumes that another user has performed a conflicting operation and aborts the current transaction.

Wenn ein anderer Benutzer einen in Konflikt stehenden Vorgang ausgeführt hat, spricht man von einem Nebenläufigkeitskonflikt.The situation when another user has performed an operation that conflicts with the current operation is known as concurrency conflict.

Die Implementierung des Vergleichs der Parallelitätstokenwerte ist Aufgabe der Datenbankanbieter.Database providers are responsible for implementing the comparison of concurrency token values.

Für relationale Datenbanken lässt sich in EF Core eine Überprüfung des Parallelitätstokenwerts in der WHERE-Klausel einer jeden UPDATE- oder DELETE-Anweisung ausführen.On relational databases EF Core includes a check for the value of the concurrency token in the WHERE clause of any UPDATE or DELETE statements. Nach dem Ausführen der Anweisungen liest EF Core die Anzahl der Zeilen, die betroffen sind.After executing the statements, EF Core reads the number of rows that were affected.

Wenn keine Zeilen betroffen sind, wird ein Nebenläufigkeitskonflikt erkannt, und EF Core löst DbUpdateConcurrencyException aus.If no rows are affected, a concurrency conflict is detected, and EF Core throws DbUpdateConcurrencyException.

Angenommen, wir möchten LastName als Parallelitätstoken von Person konfigurieren.For example, we may want to configure LastName on Person to be a concurrency token. Alle Updatevorgänge von „Person“ enthalten dann eine Parallelitätsüberprüfung in der WHERE-Klausel:Then any update operation on Person will include the concurrency check in the WHERE clause:

UPDATE [Person] SET [FirstName] = @p1
WHERE [PersonId] = @p0 AND [LastName] = @p2;

Beheben von NebenläufigkeitskonfliktenResolving concurrency conflicts

Setzen wir das vorherige Beispiel fort: Wenn ein Benutzer versucht, Änderungen an Person zu speichern, ein anderer Benutzer LastName jedoch bereits geändert hat, wird eine Ausnahme ausgelöst.Continuing with the previous example, if one user tries to save some changes to a Person, but another user has already changed the LastName, then an exception will be thrown.

Die Anwendung sollte den Benutzer einfach darüber informieren, dass das Update aufgrund von in Konflikt stehenden Änderungen nicht durchgeführt werden konnte war, und fortfahren.At this point, the application could simply inform the user that the update was not successful due to conflicting changes and move on. Es kann sich jedoch auch empfehlen, den Benutzer aufzufordern, sicherzustellen, dass dieser Datensatz weiterhin dieselbe Person darstellt, und den Vorgang zu wiederholen.But it may be desirable to prompt the user to ensure this record still represents the same actual person and to retry the operation.

So lässt sich z.B. ein Nebenläufigkeitskonflikt beheben.This process is an example of resolving a concurrency conflict.

Dazu müssen Sie die ausstehenden Änderungen aus dem aktuellen DbContext mit den Werten in der Datenbank zusammenführen.Resolving a concurrency conflict involves merging the pending changes from the current DbContext with the values in the database. Welche Werte zusammengeführt werden, hängt von der jeweiligen Anwendung ab und kann durch die Benutzereingabe gesteuert sein.What values get merged will vary based on the application and may be directed by user input.

Nebenläufigkeitskonflikte können mit drei verschiedenen Wertetypen gelöst werden:There are three sets of values available to help resolve a concurrency conflict:

  • Aktuelle Werte sind die Werte, die die Anwendung in die Datenbank schreiben wollte.Current values are the values that the application was attempting to write to the database.

  • Ursprüngliche Werte sind die Werte, die vor den Änderungen aus der Datenbank abgerufen wurden.Original values are the values that were originally retrieved from the database, before any edits were made.

  • Datenbankwerte sind die Werte, die derzeit in der Datenbank gespeichert sind.Database values are the values currently stored in the database.

Nebenläufigkeitskonflikte werden im Allgemeinen folgendermaßen behoben:The general approach to handle a concurrency conflicts is:

  1. Fangen Sie DbUpdateConcurrencyException während SaveChanges ab.Catch DbUpdateConcurrencyException during SaveChanges.
  2. Bereiten Sie mit DbUpdateConcurrencyException.Entries neue Änderungen für die betroffenen Entitäten vor.Use DbUpdateConcurrencyException.Entries to prepare a new set of changes for the affected entities.
  3. Aktualisieren Sie die ursprünglichen Werte des Parallelitätstokens, sodass sie mit den aktuellen Werten in der Datenbank übereinstimmen.Refresh the original values of the concurrency token to reflect the current values in the database.
  4. Wiederholen Sie den Prozess, bis keine Konflikte mehr auftreten.Retry the process until no conflicts occur.

Im folgenden Beispiel werden Person.FirstName und Person.LastName als Parallelitätstoken eingerichtet.In the following example, Person.FirstName and Person.LastName are setup as concurrency tokens. Dort, wo Sie die anwendungsspezifische Logik platzieren, nach der der zu speichernde Wert ausgewählt wird, befindet sich ein // TODO:-Kommentar.There is a // TODO: comment in the location where you include application specific logic to choose the value to be saved.

using (var context = new PersonContext())
{
    // Fetch a person from database and change phone number
    var person = context.People.Single(p => p.PersonId == 1);
    person.PhoneNumber = "555-555-5555";

    // Change the person's name in the database to simulate a concurrency conflict
    context.Database.ExecuteSqlCommand(
        "UPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 1");

    var saved = false;
    while (!saved)
    {
        try
        {
            // Attempt to save changes to the database
            context.SaveChanges();
            saved = true;
        }
        catch (DbUpdateConcurrencyException ex)
        {
            foreach (var entry in ex.Entries)
            {
                if (entry.Entity is Person)
                {
                    var proposedValues = entry.CurrentValues;
                    var databaseValues = entry.GetDatabaseValues();

                    foreach (var property in proposedValues.Properties)
                    {
                        var proposedValue = proposedValues[property];
                        var databaseValue = databaseValues[property];

                        // TODO: decide which value should be written to database
                        // proposedValues[property] = <value to be saved>;
                    }

                    // Refresh original values to bypass next concurrency check
                    entry.OriginalValues.SetValues(databaseValues);
                }
                else
                {
                    throw new NotSupportedException(
                        "Don't know how to handle concurrency conflicts for "
                        + entry.Metadata.Name);
                }
            }
        }
    }
}