Manipulando conflitos de simultaneidadeHandling Concurrency Conflicts

Observação

Esta página documenta como simultaneidade funciona no núcleo do EF e como lidar com conflitos de simultaneidade em seu aplicativo.This page documents how concurrency works in EF Core and how to handle concurrency conflicts in your application. Consulte Tokens de simultaneidade para obter detalhes sobre como configurar os tokens de simultaneidade em seu modelo.See Concurrency Tokens for details on how to configure concurrency tokens in your model.

Dica

Veja o exemplo deste artigo no GitHub.You can view this article's sample on GitHub.

Simultaneidade de banco de dados refere-se a situações em que vários processos ou usuários acessarem ou alterem os mesmos dados em um banco de dados ao mesmo tempo.Database concurrency refers to situations in which multiple processes or users access or change the same data in a database at the same time. Controle de simultaneidade refere-se aos mecanismos específicos usados para garantir a consistência de dados na presença de alterações simultâneas.Concurrency control refers to specific mechanisms used to ensure data consistency in presence of concurrent changes.

Implementa Core EF controle de simultaneidade otimista, que significa que ela permitirá que vários processos ou usuários alterarem independentemente, sem a sobrecarga de sincronização ou de bloqueio.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. Em uma situação ideal, essas alterações não irá interferir com o outro e, portanto, poderá ser bem-sucedida.In the ideal situation, these changes will not interfere with each other and therefore will be able to succeed. Na pior das hipóteses, dois ou mais processos tentará fazer alterações em conflito e apenas um deles deve ter êxito.In the worst case scenario, two or more processes will attempt to make conflicting changes, and only one of them should succeed.

Como funciona o controle de simultaneidade no núcleo do EFHow concurrency control works in EF Core

As propriedades configuradas como tokens de simultaneidade são usados para implementar o controle de simultaneidade otimista: sempre que uma operação de atualização ou exclusão é executada durante a SaveChanges, o valor do token de simultaneidade no banco de dados é comparado com o original valor lido por núcleo EF.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.

  • Se os valores coincidirem, a operação pode ser concluída.If the values match, the operation can complete.
  • Se os valores não coincidirem, o núcleo EF presume que outro usuário executou uma operação conflitante e anula a transação atual.If the values do not match, EF Core assumes that another user has performed a conflicting operation and aborts the current transaction.

A situação em que outro usuário executou uma operação que está em conflito com a operação atual é conhecida como conflito de simultaneidade.The situation when another user has performed an operation that conflicts with the current operation is known as concurrency conflict.

Provedores de banco de dados serão responsáveis por implementar a comparação de valores de token de simultaneidade.Database providers are responsible for implementing the comparison of concurrency token values.

Em bancos de dados relacionais EF Core inclui uma verificação para o valor do token de simultaneidade no WHERE cláusula de qualquer UPDATE ou DELETE instruções.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. Depois de executar as instruções, Core EF lê o número de linhas afetadas.After executing the statements, EF Core reads the number of rows that were affected.

Se nenhuma linha for afetada, será detectado um conflito de simultaneidade e EF Core lança DbUpdateConcurrencyException.If no rows are affected, a concurrency conflict is detected, and EF Core throws DbUpdateConcurrencyException.

Por exemplo, é aconselhável configurar LastName em Person um token de simultaneidade.For example, we may want to configure LastName on Person to be a concurrency token. Qualquer operação de atualização na pessoa incluirá a verificação de simultaneidade no WHERE cláusula: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;

Resolvendo conflitos de simultaneidadeResolving concurrency conflicts

Continuando com o exemplo anterior, se um usuário tenta salvar algumas alterações em um Person, mas outro usuário já foi alterado o LastName a uma exceção será lançada.Continuing with the previous example, if one user tries to save some changes to a Person, but another user has already changed the LastName the an exception will be thrown.

Neste ponto, o aplicativo simplesmente pode informar ao usuário que a atualização não teve êxito devido a alterações conflitantes e Avançar.At this point, the application could simply inform the user that the update was not successful due to conflicting changes and move on. Mas, talvez seja conveniente para solicitar ao usuário para garantir que este registro ainda representa a mesma pessoa real e tente a operação novamente.But it may be desirable to prompt the user to ensure this record still represents the same actual person and to retry the operation.

Esse processo é um exemplo de resolver um conflito de simultaneidade.This process is an example of resolving a concurrency conflict.

Resolver um conflito de simultaneidade mesclando as alterações pendentes do atual DbContext com os valores no banco de dados.Resolving a concurrency conflict involves merging the pending changes from the current DbContext with the values in the database. Quais valores são mesclados irão variar com base no aplicativo e podem ser direcionado pela entrada do usuário.What values get merged will vary based on the application and may be directed by user input.

Há três conjuntos de valores disponíveis para ajudar a resolver um conflito de simultaneidade:There are three sets of values available to help resolve a concurrency conflict:

  • Valores atuais são os valores que o aplicativo estava tentando gravar no banco de dados.Current values are the values that the application was attempting to write to the database.

  • Valores originais são os valores que foram originalmente recuperados do banco de dados, antes de todas as edições feitas.Original values are the values that were originally retrieved from the database, before any edits were made.

  • Valores de banco de dados são os valores atualmente armazenados no banco de dados.Database values are the values currently stored in the database.

A abordagem geral para lidar com um conflito de simultaneidade é:The general approach to handle a concurrency conflicts is:

  1. Catch DbUpdateConcurrencyException durante SaveChanges.Catch DbUpdateConcurrencyException during SaveChanges.
  2. Use DbUpdateConcurrencyException.Entries para preparar um novo conjunto de alterações para as entidades afetadas.Use DbUpdateConcurrencyException.Entries to prepare a new set of changes for the affected entities.
  3. Atualize os valores originais do token de simultaneidade para refletir os valores atuais no banco de dados.Refresh the original values of the concurrency token to reflect the current values in the database.
  4. Repita o processo até que nenhum conflito ocorre.Retry the process until no conflicts occur.

No exemplo a seguir, Person.FirstName e Person.LastName são configurados como tokens de simultaneidade.In the following example, Person.FirstName and Person.LastName are setup as concurrency tokens. Há um // TODO: comentário no local onde você incluir lógica específica do aplicativo para escolher o valor a ser salvo.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);
                }
            }
        }
    }
}