Back-End

17 dez, 2015

Curso Entity Framework – tratando a concorrência

Publicidade

Neste artigo vou apresentar os conceitos sobre a concorrência de dados na utilização do Entity Framework (se quiser ver as partes anteriores, acesse este link).

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 03 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.

ef_curb241

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

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:

ef_curb242

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

ef_curb244

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.

ef_curb245

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 o Usuario1 e Usuario2, obtendo o mesmo aluno e atualizando o nome do aluno ao mesmo tempo.

Preparando o ambiente

Vamos usar a solução criada nesta aula 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, informando, em seguida, o nome EF6_TestandoConcorrencia:

ef_curb246

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

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

ef_curb247

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 negrito 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 =&gt; s.AlunoId == 1).Single();
            }
            //Usuario2 pega o mesmo Aluno
            using (var context = new EscolaDBEntities())
            {
                context.Configuration.ProxyCreationEnabled = false;
                Usuario2 = context.Alunos.Where(s =&gt; 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
            <strong>using (var context = new EscolaDBEntities())</strong>
<strong>            {</strong>
<strong>                try</strong>
<strong>                {</strong>
<strong>                    context.Entry(Usuario1).State = EntityState.Modified;</strong>
<strong>                    context.SaveChanges();</strong>
<strong>                }</strong>
<strong>                catch (DbUpdateConcurrencyException ex)</strong>
<strong>                {</strong>
<strong>                    Console.WriteLine("Usuario1  :: Ocorreu uma exceção de concorrência Otimista : " + ex.Message);</strong>
<strong>                }</strong>
<strong>            }</strong>
            //Usuario2 salva as alterações depois do Usuario1
            //Usuario2 vai obter uma exceção de concorrência
            //porque CreateOrModifiedDate é diferente no banco de dados
            <strong>using (var context = new EscolaDBEntities())</strong>
<strong>            {</strong>
<strong>                try</strong>
<strong>                {</strong>
<strong>                    context.Entry(Usuario2).State = EntityState.Modified;</strong>
<strong>                    context.SaveChanges();</strong>
<strong>                }</strong>
<strong>                catch (DbUpdateConcurrencyException ex)</strong>
<strong>                {</strong>
<strong>                    Console.WriteLine("Usuario2  :: Ocorreu uma exceção de concorrência Otimista : " + ex.Message);</strong>
<strong>                }</strong>
<strong>            }</strong>
        }
    }
}

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

ef_curb248

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