EF Core - 3 Dicas para um bom desempenho
Neste artigo veremos 3 dicas para obter um bom desempenho com o Entity Framework Core. |
O desempenho do banco de dados é um
tópico vasto e complexo, abrangendo uma pilha inteira de componentes: o banco de
dados, a rede, o driver de banco de dado e as camadas de acesso aos dados como o
EF Core.
O EF Core é usado para fornecer uma camada de abstração entre o mundo relacional e o mundo dos objetos permitindo que o desenvolvedor abstraia muitos conceitos do mundo dos banco de dados relacionais focando na programação orientada a objetos.
Assim, podemos usar o
EF Core com esse objetivo mas não podemos relegar a segundo plano outros
quesitos importantes como desempenho.
Para mostrar os exemplos das dicas, eu vou usar um projeto Console onde já temos definidos o EF Core 5.0 e o mapeamento realizado usando a Fluent API para as entidades : Pedido, Item, Cliente e Produto.
Nota:
Para detalhes da criação do projeto e do mapeamento usando a
Fluent API veja o seguinte artigo :
EF
Core
-
Sistema de Vendas : Usando a Fluent API
Assim o nosso modelo de entidades possui as seguintes definições e associações :
A seguir vou apresentar 3 dicas básicas que com certeza vão melhorar o
desempenho das consultas geradas pelo EF Core.
1- Obtenha somente os dados que você precisa
Um dos erros básicos nas consultas LINQ usadas nas aplicações com EF Core é precisar de uma quantidade x de dados e tratar com uma quantidade muito maior. Assim se sua consulta produz muito mais dados do que você precisa isso é um desperdício.
a- Exemplo de consulta RUIM
private static void Main(string[] args) { using var contextoDb = new ApplicationDbContext();
var clientes = contextoDb
var primeiroCliente = clientes[0]; Console.WriteLine($" - {primeiroCliente.Nome} \n - {segundoCliente.Nome}"); |
Na consulta usada neste exemplo estamos retornando todos clientes e seus dados apenas para obter dois clientes.
b- Exemplo de consulta MELHOR
private static void Main(string[] args) { using var contextoDb = new ApplicationDbContext();
var clientes = contextoDb var primeiroCliente = clientes[0]; Console.WriteLine($" - {primeiroCliente.Nome} \n - {segundoCliente.Nome}"); |
Usando o operador Take(2)
estamos obtendo apenas dois clientes como desejamos.
Outros operadores que podemos usar para restringir a quantidade de dados nas consultas :
Take(n) - Selecionar os primeiros n objetos de uma coleção;
Skip(n) - Ignora o(s) primeiro(s) n objetos de uma coleção;
First - Retorna o primeiro elemento da seqüência. Lança uma exception se não for encontrados dados com critério usado;
FirstOrDefault - Retorna o primeiro elemento da seqüência, ou o valor padrão se a seqüência esta vazia.
Single - Retorna o único elemento de um seqüência única. Lança uma exception se não for encontrados dados com critério usado;
SingleOrDefault - Retorna o único elemento de uma seqüência , ou valor padrão se estiver vazia. Se mais de uma correspondência for encontrada lança uma exception;
2- Considere o uso de IQueryable e IEnumerable
O primeiro ponto importante a destacar é que a interface
IQueryable herda de
IEnumerable , de forma que tudo que IEnumerable pode fazer,
IQueryable também pode.
- IEnumerable<T> é ótimo para
trabalhar com sequências que são iteradas na memória;
- IQueryable<T> permite
trabalhar sem usar a memória, como uma fonte de dados remota, como um banco de
dados ou serviço da web;
- IQueryable é uma definição de consulta e não será carregado se não for necessário. Estender este objeto apenas altera a forma como o EF gera o SQL.
- IEnumerable é uma coleção de dados que podem ser
enumerados e ela será carregada assim que você acessá-lo.
A Grande diferença entre IEnumerable e IQueryable é onde o filtro é executado. O primeiro executa no cliente e outro executa no banco de dados.
Assim você tem que decidir quando usar IQueryable ou IEnumerable dependendo do cenário.
Resumindo:
-
IEnumerable é indicando quando todas as consultas
são realizadas na memória.
- IEnumerable é usado principalmente para LINQ to Object e
LINQ to XML.
- Ao consultar dados do banco de dados, IEnumerable executa
"consulta de seleção" no lado do servidor, carrega os dados na
memória no lado do cliente e, em seguida, filtra os dados;
-
IQueryable permite estender as consultas;
- Ao consultar dados de um banco de dados, IQueryable
executa uma "consulta de seleção" no lado do servidor com todos os
filtros;
3- Carregar entidades relacionadas com adiantamento quando possível
Ao lidar com entidades relacionadas, geralmente sabemos com antecedência o que precisamos carregar: um exemplo típico seria carregar um determinado conjunto de Clientes, juntamente com todos seus Pedidos.
Nesses cenários, é sempre melhor usar o carregamento adiantado ou eager loading para que o EF possa buscar todos os dados necessários em um único ida e volta.
O recurso filtered include, introduzido no EF Core 5,0, também permite que você limite quais entidades relacionadas você gostaria de carregar, mantendo o processo de carregamento adiantado factível em um único ida e volta.
A seguir temos um exemplo usando o novo recurso Filtered Include.
Ao usar o include
para carregar dados relacionados, você pode adicionar certas operações
enumeráveis à navegação de coleção incluída, o que permite a filtragem e
classificação dos resultados.
As operações com suporte são: Where, OrderBy,
OrderByDescending, ThenBy, ThenByDescending, Skip e Take.
Tais operações devem ser aplicadas na navegação da coleção no
lambda passado para o método
Include, conforme mostrado no exemplo
abaixo:
private static void Main(string[] args) { using var contextoDb = new ApplicationDbContext();
var clientesPedidos = contextoDb.Clientes
Console.ReadLine(); |
E estamos conversados...
"Confessai as vossas culpas uns aos outros, e orai uns pelos outros, para
que sareis. A oração feita por um justo pode muito em seus efeitos."
Tiago 5:16
Referências: