EF Core - Comparando IQueryable com métodos assíncronos


Hoje vamos entender como usar IQueryable com métodos assíncronos na plataforma .NET usando C#.

A interface IQueryable<T> herda da interface IEnumerable e se parece com qualquer coleção (IEnumerable) por fora, mas age de maneira muito diferente.

Vamos supor que você tenha definido em seu repositório uma consulta usando IQueryable da seguinte forma:

public  IQueryable<Aluno> GetAlunos()
{     
      return context.Alunos.AsQueryable();
}

Aqui  estamos usando o método AsQueryable  que permite que a consulta seja convertida em uma instância de IQueryable.

Quando você usa o operador AsQueryable em uma consulta existente e aplica outras transformações, como aplicar um filtro ou especificar uma ordem de classificação, essas instruções lambda são convertidas em árvores de expressão.

Agora suponha que no seu controlador você vai usar a seguinte consulta:


 var alunos = repoAlunos.GetAlunos().Where(a => a.Nome.Contains("J")).Take(5).ToList();
 

Vamos entender o que esta acontecendo aqui:

  1. Usando repoAlunos.GetAlunos() estamos obtendo um objeto IQueryable e não estamos ainda acessando o banco de dados;
  2. Criamos um novo objeto IQueryable especificando uma nova condição : Where(a => a.Nome.Contains("J"));
  3. Criamos um novo objeto IQueryable especificando um limite para obter 5 elementos usando Take(5);
  4. Retornamos os resultados a partir do banco de dados usando .ToList();

Ao final nosso objeto IQueryable será compilado para o SQL com a seguinte consulta: 'select top 5 * from Alunos Where... '

Vamos comparar agora com uma abordagem onde vamos usar um método assíncrono.

Agora a consulta no repositório será definida da seguinte forma:

 public async Task<IQueryable<Aluno>> GetAlunosAsync()
 {
        var alunos = await context.Alunos.ToListAsync();
        return alunos.AsQueryable();
  }

Agora vejamos o que temos aqui:

1- Agora estamos carregando na memória todos os dados dos alunos armazenados no banco de dados através da consulta :   await context.Alunos.ToListAsync();

2- Dependendo da quantidade de dados armazenados podemos até ter um falha de memória;

Então todo o cuidado é pouco quando formos definir uma consulta que retorna dados.

E como podemos fazer quando desejamos realizar filtros ou paginação em consultas com acesso assíncrono ?

Uma forma seria fazer o seguinte:

1- Definir a consulta usando IQueryable:

public  IQueryable<Aluno> GetAlunos()
{     
      return context.Alunos.AsQueryable();
}

E a seguir podemos criar a outra consulta onde iremos paginar ou filtrar de forma assíncrona usando a consulta que retorna IQueryable:

public async Task<List<Aluno>> GetAlunosPorCodigo(int codigo)
{
     return await GetAlunos().Where(u => u.Id == codigo).ToListAsync();
 }

Naturalmente não vamos ter que fazer isso se não precisarmos fazer uma paginação ou aplicar um filtro.

Pegue o projeto aqui : EFCore_IQueryable.zip

"E ouvi uma grande voz do céu, que dizia: Eis aqui o tabernáculo de Deus com os homens, pois com eles habitará, e eles serão o seu povo, e o mesmo Deus estará com eles, e será o seu Deus."
Apocalipse 21:3


José Carlos Macoratti