Behandeln von ParallelitätHandling Concurrency

Wenn eine Eigenschaft als ein parallelitätstoken konfiguriert ist prüft EF dann, dass kein anderer Benutzer beim Speichern der Änderungen an diesen Datensatz der Wert in der Datenbank nicht geändert wurde.If a property is configured as a concurrency token then EF will check that no other user has modified that value in the database when saving changes to that record.

Tipp

Sie können anzeigen, dass dieser Artikel Beispiel auf GitHub.You can view this article's sample on GitHub.

Funktionsweise der Parallelitätsbehandlung in EF CoreHow concurrency handling works in EF Core

Eine ausführliche Beschreibung der Funktionsweise der Parallelitätsbehandlung in Entity Framework Core, finden Sie unter Parallelitätstoken.For a detailed description of how concurrency handling works in Entity Framework Core, see Concurrency Tokens.

Auflösen von ParallelitätskonfliktenResolving concurrency conflicts

Auflösen von einem Parallelitätskonflikt umfasst mithilfe eines Algorithmus, um die ausstehenden Änderungen aus dem aktuellen Benutzer mit den Änderungen in der Datenbank zusammenzuführen.Resolving a concurrency conflict involves using an algorithm to merge the pending changes from the current user with the changes made in the database. Die genaue Vorgehensweise richten sich nach der Anwendung, aber eine gängige Methode ist die Werte für den Benutzer anzeigen und entscheiden, die richtigen Werte in der Datenbank gespeichert werden.The exact approach will vary based on your application, but a common approach is to display the values to the user and have them decide the correct values to be stored in the database.

Es gibt drei Gruppen von Werten verfügbar, in denen einen Parallelitätskonflikt gelöst wird.There are three sets of values available to help resolve a concurrency conflict.

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

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

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

Behandeln Sie einen Parallelitätskonflikt Abfangen einer DbUpdateConcurrencyException während SaveChanges(), verwenden DbUpdateConcurrencyException.Entries einen neuen Satz von Änderungen für die betroffenen Entitäten vorbereiten, und wiederholen dann die SaveChanges() Vorgang.To handle a concurrency conflict, catch a DbUpdateConcurrencyException during SaveChanges(), use DbUpdateConcurrencyException.Entries to prepare a new set of changes for the affected entities, and then retry the SaveChanges() operation.

Im folgenden Beispiel Person.FirstName und Person.LastName Setup als parallelitätstoken sind.In the following example, Person.FirstName and Person.LastName are setup as concurrency token. Es ist ein // TODO: Kommentar in den Speicherort, in denen schließen Sie anwendungsspezifische Logik zum Auswählen des Wert in der Datenbank gespeichert werden sollen.There is a // TODO: comment in the location where you would include application specific logic to choose the value to be saved to the database.

using Microsoft.EntityFrameworkCore;
using System;
using System.ComponentModel.DataAnnotations;
using System.Linq;

namespace EFSaving.Concurrency
{
    public class Sample
    {
        public static void Run()
        {
            // Ensure database is created and has a person in it
            using (var context = new PersonContext())
            {
                context.Database.EnsureDeleted();
                context.Database.EnsureCreated();

                context.People.Add(new Person { FirstName = "John", LastName = "Doe" });
                context.SaveChanges();
            }

            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 persons name in the database (will cause a concurrency conflict)
                context.Database.ExecuteSqlCommand("UPDATE dbo.People SET FirstName = 'Jane' WHERE PersonId = 1");

                try
                {
                    // Attempt to save changes to the database
                    context.SaveChanges();
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    foreach (var entry in ex.Entries)
                    {
                        if (entry.Entity is Person)
                        {
                            // Using a NoTracking query means we get the entity but it is not tracked by the context
                            // and will not be merged with existing entities in the context.
                            var databaseEntity = context.People.AsNoTracking().Single(p => p.PersonId == ((Person)entry.Entity).PersonId);
                            var databaseEntry = context.Entry(databaseEntity);

                            foreach (var property in entry.Metadata.GetProperties())
                            {
                                var proposedValue = entry.Property(property.Name).CurrentValue;
                                var originalValue = entry.Property(property.Name).OriginalValue;
                                var databaseValue = databaseEntry.Property(property.Name).CurrentValue;

                                // TODO: Logic to decide which value should be written to database
                                // entry.Property(property.Name).CurrentValue = <value to be saved>;

                                // Update original values to
                                entry.Property(property.Name).OriginalValue = databaseEntry.Property(property.Name).CurrentValue;
                            }
                        }
                        else
                        {
                            throw new NotSupportedException("Don't know how to handle concurrency conflicts for " + entry.Metadata.Name);
                        }
                    }

                    // Retry the save operation
                    context.SaveChanges();
                }
            }
        }

        public class PersonContext : DbContext
        {
            public DbSet<Person> People { get; set; }

            protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
            {
                optionsBuilder.UseSqlServer(@"Server=(localdb)\mssqllocaldb;Database=EFSaving.Concurrency;Trusted_Connection=True;");
            }
        }

        public class Person
        {
            public int PersonId { get; set; }

            [ConcurrencyCheck]
            public string FirstName { get; set; }

            [ConcurrencyCheck]
            public string LastName { get; set; }

            public string PhoneNumber { get; set; }
        }

    }
}