Obsługa współbieżnościHandling Concurrency

Jeśli właściwość jest skonfigurowana jako tokenu współbieżności następnie EF sprawdzi żaden inny użytkownik zmodyfikował tę wartość w bazie danych podczas zapisywania zmian w tym rekordzie.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.

Porada

Można wyświetlić w tym artykule próbki w witrynie GitHub.You can view this article's sample on GitHub.

Jak działa Obsługa współbieżności w EF CoreHow concurrency handling works in EF Core

Aby uzyskać szczegółowy opis działania Obsługa współbieżności w Entity Framework Core, zobacz tokeny współbieżności.For a detailed description of how concurrency handling works in Entity Framework Core, see Concurrency Tokens.

Rozwiązywanie konfliktów współbieżnościResolving concurrency conflicts

Rozwiązywanie konfliktów współbieżności obejmuje przy użyciu algorytmu, aby scalić zmiany oczekujące z bieżącego użytkownika z zmiany wprowadzone w bazie danych.Resolving a concurrency conflict involves using an algorithm to merge the pending changes from the current user with the changes made in the database. Dokładne podejście różni się zależnie od aplikacji, ale typowym podejściem jest wyświetlany wartości dla użytkownika i je określić poprawnych wartości, które mają być przechowywane w bazie danych.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.

Istnieją trzy zestawy wartości może pomóc rozwiązać konflikt współbieżności.There are three sets of values available to help resolve a concurrency conflict.

  • Bieżące wartości wartości próby zapisu w bazie danych aplikacji.Current values are the values that the application was attempting to write to the database.

  • Oryginalne wartości to wartości, które zostały pierwotnie pobrany z bazy danych, aby zmiany zostały wprowadzone.Original values are the values that were originally retrieved from the database, before any edits were made.

  • Baza danych wartości wartości przechowywane w bazie danych.Database values are the values currently stored in the database.

Aby obsługiwać konflikt współbieżności, catch DbUpdateConcurrencyException podczas SaveChanges(), użyj DbUpdateConcurrencyException.Entries przygotować nowy zestaw zmian do odpowiednich jednostek, a następnie spróbuj ponownie SaveChanges() operacji.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.

W poniższym przykładzie Person.FirstName i Person.LastName są skonfigurowane jako token współbieżności.In the following example, Person.FirstName and Person.LastName are setup as concurrency token. Brak // TODO: — komentarz w lokalizacji, w którym można dołączyć określonego logikę aplikacji, aby wybrać wartość do zapisania w bazie danych.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; }
        }

    }
}