Curso Entity Framework - Tratando a Concorrência - XXIV


  Nesta aula vou apresentar os conceitos sobre a concorrência de dados na utilização do Entity Framework. (aula anterior)

 

Chegou o Curso ASP .NET MVC 5 Vídeo Aulas (C#)

Clique e Confira
 

O Entity Framework suporta a concorrência otimista por padrão, e, neste modelo o EF salva a entidade para o banco de dados assumindo que os dados não foram alterados desde que a entidade foi carregada. Se os dados forem alterados será lançada uma exceção e você precisa resolver o conflito antes de tentar salvar novamente.

Vamos ver como tratar a concorrência otimista usando a entidade Aluno do nosso modelo de entidades definido na aula 3 do curso.

Vamos incluir uma coluna com o nome RowVersion do tipo timestamp na tabela Aluno para tratar a concorrência com a entidade Aluno.

Nota: O valor de RowVersion será incrementado automaticamente pelo banco de dados durante a inclusão ou atualização.

RowVersion é um tipo de dados no SQL Server que gera automaticamente números binários únicos sempre que uma operação de atualização ou inclusão for realizada na tabela. Assim o tipo de dados rowversion é apenas um número auto incrementado sendo similar ao tipo de dados timestamp.

Após incluir a coluna tabela vamos atualizar o nosso modelo de entidades. Abra o modelo e clique com o botão direito sobre a entidade Aluno e selecione a opção Update Model From DataBase e a seguir selecione a guia Refresh e clique no botão Finish:

Após isso você deverá ver a propriedade RowVersion mapeada para a entidade Aluno:

Agora precisamos definir a propriedade Concurrency Mode para Fixed.

Clique na propriedade RowVersion na entidade Aluno e na janela de propriedades altere o valor da propriedade Concurrency Mode de None para Fixed.

Agora o Entity Framework irá incluir a coluna RowVersion na cláusula Where sempre que você realizar uma operação de atualização de dados e se o valor de rowversion for diferente do valor da cláusula where então será lançada a exceção DbUpdateConcurrencyExection.

Vamos fazer um teste usando um cenário onde um Usuario1 e Usuario2 obtendo o mesmo aluno e e atualizando o nome do aluno ao mesmo tempo:

Preparando o ambiente

Vamos usar a solução criada na aula 11 - Entity Framework - Consultas Projeção - EF6_EscolaDB - para testar o cenário da concorrência descrito acima.

Abra a solução EF6_EscolaDB e a seguir clique no menu FILE -> Add -> New Project;

Selecione a linguagem Visual C# e o template Console Application e informe o nome EF6_TestandoConcorrencia :

Para referenciar o EF no projeto incluído, no menu TOOLS clique em Nuget Package Manager -> Manage Nuget Packages for solution;

A seguir clique em Installed packages e depois no botão Manage e marque o projeto que incluímos e a seguir no botão OK.

A seguir inclua uma referência neste projeto ao projeto EF6_EscolaDB e atualize também o arquivo App.Config com a string de conexão para a entidade :

...
<
connectionStrings>
<
add name="EscolaDBEntities" connectionString="metadata=res://*/EscolaDB.csdl|res://*/EscolaDB.ssdl|res://*/EscolaDB.msl;provider=System.Data.SqlClient;provider connection string=&quot;data source=.\SQLEXPRESS;initial catalog=EscolaDB;integrated security=True;MultipleActiveResultSets=True;App=EntityFramework&quot;" providerName="System.Data.EntityClient" /></connectionStrings>
...


Testando a concorrência

A seguir no método Main() do arquivo Program.cs inclua o código abaixo :

using EF6_EscolaDB;
using System.Linq;
namespace EF6_TestandoCorrencia
{
    class Program
    {
        static void Main(string[] args)
        {
            Aluno Usuario1 = null;
            Aluno Usuario2 = null;
            //Usuario1 pega o Aluno
            using (var context = new EscolaDBEntities())
            {
                context.Configuration.ProxyCreationEnabled = false;
                Usuario1 = context.Alunos.Where(s => s.AlunoId == 1).Single();
            }
            //Usuario2 pega o mesmo Aluno
            using (var context = new EscolaDBEntities())
            {
                context.Configuration.ProxyCreationEnabled = false;
                Usuario2 = context.Alunos.Where(s => s.AlunoId == 1).Single();
            }
            //Usuario1 atualiza o nome do aluno
            Usuario1.AlunoNome = "Editado pelo Usuario1";
            //Usuario2 também atualiza o nome do aluno
            Usuario2.AlunoNome = "Editado pelo Usuario2";
        }
    }
}

Vamos agora concluir o cenário definindo que o Usuario1 salve as alterações antes do Usuario2.

Dessa forma quando o Usuario2 tentar salvar suas alterações ele vai obter um exceção causada pela concorrência.

Inclua o código destacado em azul abaixo para refletir esse cenário:

using EF6_EscolaDB;
using System;
using System.Data.Entity;
using System.Data.Entity.Infrastructure;
using System.Linq;
namespace EF6_TestandoCorrencia
{
    class Program
    {
        static void Main(string[] args)
        {
            Aluno Usuario1 = null;
            Aluno Usuario2 = null;
            //Usuario1 pega o Aluno
            using (var context = new EscolaDBEntities())
            {
                context.Configuration.ProxyCreationEnabled = false;
                Usuario1 = context.Alunos.Where(s => s.AlunoId == 1).Single();
            }
            //Usuario2 pega o mesmo Aluno
            using (var context = new EscolaDBEntities())
            {
                context.Configuration.ProxyCreationEnabled = false;
                Usuario2 = context.Alunos.Where(s => s.AlunoId == 1).Single();
            }
            //Usuario1 atualiza o nome do aluno
            Usuario1.AlunoNome = "Editado pelo Usuario1";
            //Usuario2 também atualiza o nome do aluno
            Usuario2.AlunoNome = "Editado pelo Usuario2";
            //--------------------------------------------------------------
            //Usuario1 salva as alterações primeiro
            using (var context = new EscolaDBEntities())
            {
                try
                {
                    context.Entry(Usuario1).State = EntityState.Modified;
                    context.SaveChanges();
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    Console.WriteLine("Usuario1  :: Ocorreu uma exceção de concorrência Otimista : " + ex.Message);
                }
            }
            //Usuario2 salva as alterações depois do Usuario1
            //Usuario2 vai obter uma exceção de concorrência
            //porque CreateOrModifiedDate é diferente no banco de dados
            using (var context = new EscolaDBEntities())
            {
                try
                {
                    context.Entry(Usuario2).State = EntityState.Modified;
                    context.SaveChanges();
                }
                catch (DbUpdateConcurrencyException ex)
                {
                    Console.WriteLine("Usuario2  :: Ocorreu uma exceção de concorrência Otimista : " + ex.Message);
                }
            }
        }
    }
}

Executando o projeto iremos obter uma exceção exibida no console ocorrida com o Usuario2:

Se você quer saber mais sobre o tratamento da concorrência no EF veja o meu curso na seção de cursos do site:

Entity Framework - O tratamento da concorrência - Curso

Na próxima aula veremos como usar stored procedures no Entity Framework.
 

Veja os Destaques e novidades do SUPER DVD Visual Basic (sempre atualizado) : clique e confira !

Quer migrar para o VB .NET ?

Quer aprender C# ??

 

             Gostou ?   Compartilhe no Facebook   Compartilhe no Twitter
 

Referências:


José Carlos Macoratti