Entity Framework 6 -  Novos Recursos - Consultas e Persistência Assíncronas

 Parece que o Entity Framework esta virando o jogo e esta alcançando um novo patamar de poder evoluindo de um início turbulento e resolvendo problemas de desempenho e da qualidade de código gerado.

 O Entity Framework 6.0 introduziu novos recursos interessantes para as abordagens database first e code first.

Mas não se assuste, o Entity Framework 6.0 é uma evolução e tudo o que você já aprendeu e já conhece sobre o Entity Framework não esta perdido pois ele permanece o mesmo  não havendo uma mudança no seu funcionamento mas apenas em alguns recursos.

Vejamos um resumo das principais novidades da nova versão.

1- Recursos para DataBase First e Code First

2- Recursos para Code-First

Vamos apresentar as principais características mostrando um exemplo prático de utilização.

1- Consultas e Persistência Assíncronas

Podemos agora tomar vantagem da execução assíncrona da plataforma .NET 4.5 pois o Entity Framework 6.0 tem a habilidade de executar consultas e comandos de forma assíncrona usando o DbContext.

O EF6 introduziu o suporte para consulta e a persistência assíncrona as palavras chaves async e await que foram introduzidas no. NET 4.5. Embora nem todos os aplicativos podem se beneficiar da assincronia, ele pode ser usado para melhorar a capacidade de resposta do cliente e e escalabilidade do servidor quando estiver tratando tarefas de longa duração, de rede ou tarefas de input/output.

A programação Async esta focada em liberar a thread atual para realizar outro trabalho enquanto aguarda por uma operação que não requer tempo para ser concluída a partir de uma thread gerenciada. Exemplo: Enquanto o banco de dados esta processando a consulta não há nada que o código pode fazer nada.

Em aplicações cliente (WinForms, WPF, etc) a thread atual pode ser usada para manter a capacidade de resposta da interface do usuário, enquanto a operação assíncrona é executada. Em aplicações de servidor (ASP.NET, etc), a thread pode ser usada para processar outros pedidos recebidos - o que pode reduzir o uso de memória e/ou aumentar o rendimento do servidor.

Na maioria das aplicações a utilização dos recursos assíncronos não terá benefícios visíveis e até mesmo pode ser prejudicial. Use testes, perfis e bom senso para medir o impacto na utilização deste recursos em seu cenário particular antes de utilizá-lo.

No exemplo deste artigo vamos usar uma abordagem Code-First mas qualquer outra abordagem pode ser usada com os mesmos resultados.

Criando o modelo no VS 2013

1- Executando no modo Síncrono

Abra o Visual Studio 2013 Express for Windows Desktop e clique em New Project;

Selecione o template Visual C# -> Windows -> Console Application e informe o nome Demo_Async e clique em OK;

A seguir vamos incluir o pacote do Entity Framework usando Nuget :

Vamos incluir uma classe chamada Model.cs onde iremos definir o nosso modelo de entidades.

No menu PROJECT clique em Add Class informe o nome Model.cs e clique em OK;

Agora defina o seguinte código nesta classe:

using System.Collections.Generic;
using System.Data.Entity; 
namespace Demo_Async
{
        public class BloggingContext : DbContext
        {
            public DbSet<Blog> Blogs { get; set; }
            public DbSet<Post> Posts { get; set; }
        }
        public class Blog
        {
            public int BlogId { get; set; }
            public string Name { get; set; }
            public virtual List<Post> Posts { get; set; }
        }
        public class Post
        {
            public int PostId { get; set; }
            public string Title { get; set; }
            public string Content { get; set; }
            public int BlogId { get; set; }
            public virtual Blog Blog { get; set; }
        } 
}
	Public Class BloggingContext
		Inherits DbContext
		Public Property Blogs() As DbSet(Of Blog)
		Private m_Posts As DbSet(Of Post)
	End Class
	Public Class Blog
		Public Property BlogId() As Integer
		Public Property Name() As String
		Public Overridable Property Posts() As List(Of Post)
		Private Overridable m_Posts As List(Of Post)
	End Class
	Public Class Post
		Public Property PostId() As Integer
		Public Property Title() As String
		Public Property Content() As String
		Public Property BlogId() As Integer
		Public Overridable Property Blog() As Blog
	End Class

 

C# VB .NET

A seguir vamos definir o código no método Main() da classe Program.cs para usar o Modelo que criamos acima:

 static void Main(string[] args)
 {
            RealizarOperacoesDoBancodeDados();
            Console.WriteLine();
            Console.WriteLine("Frase do Dia:");
            Console.WriteLine(" Não se preocupe se o mundo vai acabar hoje... ");
            Console.WriteLine(" Já é amanhã no Japão.");
            Console.WriteLine();
            Console.WriteLine("Pressione algo para sair...");
            Console.ReadKey(); 
 }
Private Shared Sub Main(args As String())

	RealizarOperacoesDoBancodeDados()
	Console.WriteLine()
	Console.WriteLine("Frase do Dia:")
	Console.WriteLine(" Não se preocupe se o mundo vai acabar hoje... ")
	Console.WriteLine(" Já é amanhã no Japão.")
	Console.WriteLine()
	Console.WriteLine("Pressione algo para sair...")
	Console.ReadKey()
End Sub

 

C# VB .NET

O código para a rotina RealizarOperacoesDoBancodeDados() é visto a seguir:

 public static void RealizarOperacoesDoBancodeDados()
        {
            using (var db = new BloggingContext())
            {
                // Cria um novo blog e o salva
                db.Blogs.Add(new Blog
                {
                       Name = "Testando o Blog #" + (db.Blogs.Count() + 1)
                });
                db.SaveChanges();
                // Consulta todos os blogs ordenados por nome
                var blogs = (from b in db.Blogs
                             orderby b.Name
                             select b).ToList();
                // Exibe todos os blogs no Console 
                Console.WriteLine();
                Console.WriteLine("Todos os blogs:");
                foreach (var blog in blogs)
                {
                    Console.WriteLine(" " + blog.Name);
                }
            }
        }
Public Shared Sub RealizarOperacoesDoBancodeDados()

	Using db = New BloggingContext()

		' Cria um novo blog e o salva
		db.Blogs.Add(New Blog() With { _
			 .Name = "Testando o Blog #" + (db.Blogs.Count() + 1) _
		})
		db.SaveChanges()

		' Consulta todos os blogs ordenados por nome
		Dim blogs = (From b In db.BlogsOrder By b.Nameb).ToList()

		' Exibe todos os blogs no Console 
		Console.WriteLine()
		Console.WriteLine("Todos os blogs:")
		For Each blog As var In blogs
			Console.WriteLine(" " + blog.Name)
		Next
	End Using

End Sub

 

C# VB .NET

Este código chama o método RealizarOperacoesDoBancodeDados o qual salva um novo Blog no banco de dados e então retornar todos os blogs a partir do banco de dados e os exibe no console. Depois disso o programa escreve a frase do dia no console.

Como o código é síncrono podemos observar o seguinte fluxo de execução ao executar o programa:

  1. O método SaveChanges inicia a persistência de um novo Blog no banco de dados;
  2. O método SaveChanges termina;
  3. Uma consulta para obter todos os Blogs é enviada ao banco de dados;
  4. A consulta retorna os resultados e os escreve no  Console;
  5. A frase do dia é escrita no Console;

2- Executando no modo Assíncrono

Vamos agora ajustar o nosso exemplo para rodar no modo Assíncrono usando as novas palavras-chave async e await.

Para isso temos que fazer as seguintes alterações na classe Program.cs :

  1. Incluir o namespace System.Data.Entity para ter acesso aos métodos de extensão async do Entity Framework;
  2. Incluir o namespace  System.Threading.Tasks que permite usar o tipo Task;

Alterar o código do método Main() conforme abaixo:

 static void Main(string[] args)
        {
            //RealizarOperacoesDoBancodeDados();
            var tarefa = RealizarOperacoesDoBancodeDados();
            Console.WriteLine();
            Console.WriteLine("Frase do Dia:");
            Console.WriteLine(" Não se preocupe sobre se o fim do mundo vai ser hoje... ");
            Console.WriteLine(" Já é amanhã no Japão.");
            tarefa.Wait();
            Console.WriteLine();
            Console.WriteLine("Pressione algo para sair...");
            Console.ReadKey(); 
  }

Na linha de código

              var tarefa = RealizarOperacoesDoBancodeDados();

Estamos capturando uma tarefa que monitora o progresso do método RealizarOperacoesDoBancodeDados();

Na linha de código:

            tarefa.Wait();

Estamos bloqueando a execução do programa para esta tarefa para completar uma vez todo o trabalho para que o programa é feito.

A seguir temos o código alterada do método RealizarOperacoesDoBancodeDados();

 public static async Task RealizarOperacoesDoBancodeDados()
        {
            using (var db = new BloggingContext())
            {
                // Cria um novo blog e o salva
                db.Blogs.Add(new Blog
                {
                       Name = "Testando o Blog #" + (db.Blogs.Count() + 1)
                });
                //db.SaveChanges();
                Console.WriteLine("Chamando SaveChanges()");
                await db.SaveChangesAsync();
                Console.WriteLine("SaveChanges() terminou");
                // Consulta todos os blogs ordenados por nome
                var blogs = await (from b in db.Blogs
                                orderby b.Name
                                select b).ToListAsync();
                // Exibe todos os blogs no Console 
                Console.WriteLine("Executando a consulta");
                Console.WriteLine();
                Console.WriteLine("Todos os blogs:");
                foreach (var blog in blogs)
                {
                    Console.WriteLine(" " + blog.Name);
                }
            }
        } 

Marcamos o método como async e retornando um Task e no código chamamos a versão Async do método SaveChanges ( SaveChangesAsync ) e aguardamos sua conclusão.

Alteramos a consulta usando a versão Async para ToList (ToListAsync) e aguardamos o resultado.

Agora o código esta no modo Assíncrono e podemos observar um fluxo diferente de execução ao executar o programa:

  1. O método SaveChanges() inicia a persistência de um novo Blog no banco de dados; (Uma vez que o comando foi enviado ao banco de dados não precisamos mais esperar na thread principal. O método RealizarOperacoesDoBancodeDados retorna e o fluxo do programa no método Main() continua;
  2. A frase do dia é exibida no console. (Como não há mais trabalho no método Main(), a thread gerenciada é bloqueada na chamada a Wait até que a operação com o banco de dados termine.)
  3. SaveChanges termina;
  4. Consultar todos os blogs é enviado ao banco de dados;(A thread esta livre para realizar outro trabalho quando a consulta é processada)
  5. A consulta retorna os resultados e estes são exibidos no console;

Da mesma forma podemos também criar consultas assíncronas. Vamos criar uma consulta chamada GetBlog() que retorna um blog pelo seu Id:

private static async Task<Blog> GetBlog()
        {
            Blog _blog = null;
            using (var context = new BloggingContext())
            {
                Console.WriteLine("Iniciando GetBlog...");
                _blog = await (context.Blogs.Where(s => s.BlogId == 1).FirstOrDefaultAsync<Blog>());
                Console.WriteLine("Terminando GetBlog...");
            }
            return _blog;
        }

Como vemos no código acima o método GeBlog() esta marcado com a palavra-chave async tornando-o um método Assíncrono. Por isso o tipo de retorno do método tem que ser Task.

O método GetBlog() retorna objetos da entidade Blog por isso o tipo de retorno deve ser Task<Blog>.

Da mesma forma a consulta esta marcada com a palavra-chave await; isso libera a chamada da thread para fazer alguma outra tarefa até que a consulta retorne os dados.

Usamos o método de extensão FirstOrDefaultAsync do namespace System.Data.Entity.  (Existem outros métodos de extensão como: SingleOrDefaultAsync, ToListAsyn etc.)

Da mesma forma podemos persistir os dados de forma assíncrona criando um método para salvar as entidades conforme abaixo:

private static async Task SaveBlog(Blog blogAlterado)
 {
            using (var context = new BloggingContext())
            {
                context.Entry(blogAlterado).State = EntityState.Modified;
                Console.WriteLine("Iniciar SaveBlog...");
                int x = await (context.SaveChangesAsync());
                Console.WriteLine("Terminar SaveBlog...");
            }
 }

Pegue o projeto completo aqui: Demo_Async.zip (sem as referências ao EF)

João 5:19 Disse-lhes, pois, Jesus: Em verdade, em verdade vos digo que o Filho de si mesmo nada pode fazer, senão o que vir o Pai fazer; porque tudo quanto ele faz, o Filho o faz igualmente.

João 5:20 Porque o Pai ama ao Filho, e mostra-lhe tudo o que ele mesmo faz; e maiores obras do que estas lhe mostrará, para que vos maravilheis.

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