Consulta assíncrona e salvar
Observação
EF6 em diante apenas: os recursos, as APIs etc. discutidos nessa página foram introduzidos no Entity Framework 6. Se você estiver usando uma versão anterior, algumas ou todas as informações não se aplicarão.
O EF6 introduziu suporte para consulta assíncrona e salvamento usando as palavras-chave async e await que foram introduzidas no .NET 4.5. Embora nem todos os aplicativos possam se beneficiar da assincronia, ela pode ser usada para melhorar a capacidade de resposta do cliente e a escalabilidade do servidor ao lidar com tarefas de longa execução, de rede ou ligadas a E/S.
Quando realmente usar o assíncrono
O objetivo deste passo a passo é introduzir os conceitos assíncronos de uma forma que facilite a observação da diferença entre a execução de programas assíncronos e síncronos. Este passo a passo não se destina a ilustrar nenhum dos principais cenários em que a programação assíncrona oferece benefícios.
A programação assíncrona se concentra principalmente em liberar o thread gerenciado atual (thread executando código .NET) para fazer outro trabalho enquanto aguarda uma operação que não requer nenhum tempo de computação de um thread gerenciado. Por exemplo, enquanto o mecanismo de banco de dados está processando uma consulta, não há nada a ser feito pelo código .NET.
Em aplicativos cliente (WinForms, WPF, etc.) o thread atual pode ser usado para manter a interface do usuário responsiva enquanto a operação assíncrona é executada. Em aplicativos de servidor (ASP.NET etc.) o thread pode ser usado para processar outras solicitações de entrada - isso pode reduzir o uso de memória e/ou aumentar a taxa de transferência do servidor.
Na maioria dos aplicativos usando assíncrono não terá benefícios perceptíveis e até mesmo pode ser prejudicial. Use testes, criação de perfil e bom senso para medir o impacto do assíncrono em seu cenário específico antes de se comprometer com ele.
Aqui estão mais alguns recursos para aprender sobre assíncrono:
- Visão geral de Brandon Bray sobre async/await no .NET 4.5
- Páginas de programação assíncrona na Biblioteca MSDN
Criar o modelo
Usaremos o fluxo de trabalho Code First para criar nosso modelo e gerar o banco de dados, no entanto, a funcionalidade assíncrona funcionará com todos os modelos EF, incluindo aqueles criados com o EF Designer.
- Crie um aplicativo de console e chame-o de AsyncDemo
- Adicionar o pacote NuGet do EntityFramework
- No Gerenciador de Soluções, clique com o botão direito do mouse no projeto AsyncDemo
- Selecione Gerenciar pacotes NuGet...
- Na caixa de diálogo Gerenciar Pacotes NuGet, selecione a guia Online e escolha o pacote EntityFramework
- Clique em Instalar
- Adicione uma classe Model.cs com a seguinte implementação
using System.Collections.Generic;
using System.Data.Entity;
namespace AsyncDemo
{
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; }
}
}
Criar um programa síncrono
Agora que temos um modelo EF, vamos escrever algum código que o use para executar algum acesso a dados.
- Substitua o conteúdo de Programa.cs com o seguinte código
using System;
using System.Linq;
namespace AsyncDemo
{
class Program
{
static void Main(string[] args)
{
PerformDatabaseOperations();
Console.WriteLine("Quote of the day");
Console.WriteLine(" Don't worry about the world coming to an end today... ");
Console.WriteLine(" It's already tomorrow in Australia.");
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
public static void PerformDatabaseOperations()
{
using (var db = new BloggingContext())
{
// Create a new blog and save it
db.Blogs.Add(new Blog
{
Name = "Test Blog #" + (db.Blogs.Count() + 1)
});
Console.WriteLine("Calling SaveChanges.");
db.SaveChanges();
Console.WriteLine("SaveChanges completed.");
// Query for all blogs ordered by name
Console.WriteLine("Executing query.");
var blogs = (from b in db.Blogs
orderby b.Name
select b).ToList();
// Write all blogs out to Console
Console.WriteLine("Query completed with following results:");
foreach (var blog in blogs)
{
Console.WriteLine(" " + blog.Name);
}
}
}
}
}
Esse código chama o método PerformDatabaseOperations
que salva um novo Blog no banco de dados e, em seguida, recupera todos os Blogs do banco de dados e os imprime no Console. Depois disso, o escreve grava uma citação do dia para o Console.
Como o código é síncrono, podemos observar o seguinte fluxo de execução quando executamos o programa:
SaveChanges
começa a empurrar o novo Blog para o banco de dadosSaveChanges
completa- A consulta de todos os Blogs é enviada para o banco de dados
- Os retornos e resultados da consulta são gravados no Console
- A citação do dia está escrita em Console
Tornando-o assíncrono
Agora que temos nosso programa em funcionamento, podemos começar a fazer uso das novas palavras-chave assíncronas e aguardar. Fizemos as seguintes alterações no Programa.cs
- Linha 2: a instrução using para o namespace
System.Data.Entity
nos dá acesso aos métodos de extensão assíncrona do EF. - Linha 4: a instrução using para o namespace
System.Threading.Tasks
nos permite usar o tipoTask
. - Linhas 12 e 18: estamos capturando como tarefa que monitora o progresso de
PerformSomeDatabaseOperations
(linha 12) e, em seguida, bloqueando a execução do programa para que essa tarefa seja concluída assim que todo o trabalho para o programa for concluído (linha 18). - Linha 25: atualizamos
PerformSomeDatabaseOperations
para ser marcado comoasync
e retornar umTask
. - Linha 35: agora estamos chamando a versão assíncrona do
SaveChanges
e aguardando sua conclusão. - Linha 42: agora estamos chamando a versão assíncrona do
ToList
e aguardando o resultado.
Para obter uma lista abrangente de métodos de extensão disponíveis no namespace System.Data.Entity
, consulte a classe QueryableExtensions
. Você também precisará adicionar using System.Data.Entity
às suas instruções de uso.
using System;
using System.Data.Entity;
using System.Linq;
using System.Threading.Tasks;
namespace AsyncDemo
{
class Program
{
static void Main(string[] args)
{
var task = PerformDatabaseOperations();
Console.WriteLine("Quote of the day");
Console.WriteLine(" Don't worry about the world coming to an end today... ");
Console.WriteLine(" It's already tomorrow in Australia.");
task.Wait();
Console.WriteLine();
Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
public static async Task PerformDatabaseOperations()
{
using (var db = new BloggingContext())
{
// Create a new blog and save it
db.Blogs.Add(new Blog
{
Name = "Test Blog #" + (db.Blogs.Count() + 1)
});
Console.WriteLine("Calling SaveChanges.");
await db.SaveChangesAsync();
Console.WriteLine("SaveChanges completed.");
// Query for all blogs ordered by name
Console.WriteLine("Executing query.");
var blogs = await (from b in db.Blogs
orderby b.Name
select b).ToListAsync();
// Write all blogs out to Console
Console.WriteLine("Query completed with following results:");
foreach (var blog in blogs)
{
Console.WriteLine(" - " + blog.Name);
}
}
}
}
}
Agora que o código é assíncrono, podemos observar um fluxo de execução diferente quando executamos o programa:
SaveChanges
começa a empurrar o novo Blog para o banco de dados
Depois que o comando é enviado para o banco de dados, não é necessário mais tempo de computação no thread gerenciado atual. O métodoPerformDatabaseOperations
retorna (mesmo que não tenha terminado a execução) e o fluxo do programa no método Main continua.- A citação do dia está escrita no Console
Como não há mais trabalho a ser feito no método Main, o thread gerenciado é bloqueado na chamadaWait
até que a operação do banco de dados seja concluída. Assim que terminar, o restante do nossoPerformDatabaseOperations
será executado. SaveChanges
completa- A consulta de todos os Blogs é enviada para o banco de dados
Novamente, o thread gerenciado está livre para fazer outro trabalho enquanto a consulta é processada no banco de dados. Como todas as outras execuções foram concluídas, o thread será interrompido na chamada de espera. - Os retornos e resultados da consulta são gravados no Console
Resumo
Agora vimos como é fácil fazer uso dos métodos assíncronos do EF. Embora as vantagens da assíncrona possam não ser muito aparentes com um aplicativo de console simples, essas mesmas estratégias podem ser aplicadas em situações em que atividades de execução longa ou associadas à rede podem bloquear o aplicativo ou fazer com que um grande número de threads aumente o volume de memória.