Estou trabalhando em algumas coisas de API da Web usando o Entity Framework 6 e um dos meus métodos de controlador é um "Get All" que espera receber o conteúdo de uma tabela do meu banco de dados como IQueryable<Entity>
. No meu repositório, estou me perguntando se há algum motivo vantajoso para fazer isso de forma assíncrona, já que sou novo no uso de EF com async.
Basicamente, tudo se resume a
public async Task<IQueryable<URL>> GetAllUrlsAsync()
{
var urls = await context.Urls.ToListAsync();
return urls.AsQueryable();
}
vs
public IQueryable<URL> GetAllUrls()
{
return context.Urls.AsQueryable();
}
A versão assíncrona realmente renderá benefícios de desempenho aqui ou estou incorrendo em sobrecarga desnecessária ao projetar para uma lista primeiro (usando async, veja bem) e ENTÃO indo para IQueryable?
c#
entity-framework
async-await
Jesse Carter
fonte
fonte
Respostas:
O problema parece ser que você não entendeu como async / await funcionam com o Entity Framework.
Sobre Entity Framework
Então, vamos dar uma olhada neste código:
e exemplo de seu uso:
O que acontece lá?
IQueryable
objeto (não acessando banco de dados ainda) usandorepo.GetAllUrls()
IQueryable
objeto com a condição especificada usando.Where(u => <condition>
IQueryable
objeto com limite de paginação especificado usando.Take(10)
.ToList()
. NossoIQueryable
objeto é compilado em sql (comoselect top 10 * from Urls where <condition>
). E o banco de dados pode usar índices, o sql server envia a você apenas 10 objetos do seu banco de dados (nem todos os bilhões de urls armazenados no banco de dados)Ok, vamos dar uma olhada no primeiro código:
Com o mesmo exemplo de uso que obtivemos:
await context.Urls.ToListAsync();
.Sobre async / await
Por que async / await é preferido para usar? Vejamos este código:
o que acontece aqui?
var stuff1 = ...
userId
var stuff2 = ...
userId
Então, vamos olhar para uma versão assíncrona dele:
o que acontece aqui?
Maneira certa de fazer isso
Bom código aqui:
Observe que você deve adicionar
using System.Data.Entity
para usar o métodoToListAsync()
para IQueryable.Observe que se você não precisa de filtragem, paginação e outras coisas, não precisa trabalhar com
IQueryable
. Você pode apenas usarawait context.Urls.ToListAsync()
e trabalhar com materializadoList<Url>
.fonte
GetAllUrlsByUser
método, não precisa torná-lo assíncrono. Basta retornar a Tarefa e evitar que uma máquina de estado desnecessária seja gerada pelo compilador.async
eawait
se você NÃO estiver fazendo nada com a lista. Deixe o chamador fazerawait
isso. Ao aguardar a chamada neste estágio,return await GetAllUrls().Where(u => u.User.Id == userId).ToListAsync();
você está criando um wrapper assíncrono extra ao descompilar o assembly e examinar o IL.Há uma diferença enorme no exemplo que você postou, a primeira versão:
Isso é ruim , basicamente
select * from table
retorna, retorna todos os resultados na memória e, em seguida, aplica owhere
contra aquele na coleção de memória em vez deselect * from table where...
contra o banco de dados.O segundo método não irá realmente atingir o banco de dados até que uma consulta seja aplicada ao
IQueryable
(provavelmente através de um linq.Where().Select()
operação no estilo que retornará apenas os valores db que correspondem à consulta.Se seus exemplos forem comparáveis, a
async
versão normalmente será um pouco mais lenta por solicitação, pois há mais sobrecarga na máquina de estado que o compilador gera para permitir aasync
funcionalidade.No entanto, a principal diferença (e benefício) é que a
async
versão permite mais solicitações simultâneas, pois não bloqueia o thread de processamento enquanto aguarda a conclusão do IO (consulta de banco de dados, acesso a arquivo, solicitação da web, etc.).fonte
Resumindo,
IQueryable
é projetado para adiar o processo RUN e, em primeiro lugar, construir a expressão em conjunto com outrasIQueryable
expressões e, em seguida, interpretar e executar a expressão como um todo.Mas o
ToList()
método (ou alguns tipos de métodos como esse) são para executar a expressão instantaneamente "como está".Seu primeiro método (
GetAllUrlsAsync
), será executado imediatamente, pois éIQueryable
seguido porToListAsync()
método. portanto, ele é executado instantaneamente (assíncrono) e retorna um monte deIEnumerable
s.Enquanto isso, seu segundo método (
GetAllUrls
) não será executado. Em vez disso, ele retorna uma expressão e CALLER desse método é responsável por executar a expressão.fonte