Article image
Alvaro Costa
Alvaro Costa05/11/2022 12:10
Compartilhe

Melhorando o desempenho do Entity Framework;

  • #.NET Core
  • #.NET
  • #.NET C#

Há pouco tempo, tive um problema com o Entity Framework (uma Query que está parando em tempo de execução). Optei pela escala vertical do servidor, e não ajudou. Como resolver este problema?

Tentativa de resolver o problema da Query

Com foco no domínio e-commerce, o projeto é um sistema de gerenciamento de pedidos de um fabricante de equipamentos de móveis hospitalar.

A tabela que foi consultada com uma certa frequência é a tabela Orders. O Pedido pode ter um ou mais OrderItems. Um pedido típico contará 80 itens no pedido. Além disso, OrderItems tem uma tabela que contém as dimensões válidas - OrderItemDimensions.

DataContext
  .Orders
  .Include(o => o.OrderItems)
  .ThenInclude(oi => oi.Dimensions)
  .First(o => o.Id == orderId); 

O que vai ser enviado ao dataBase após a conversão do EF?

SELECT o.*, oi.*, d.
FROM Orders o
LEFT JOIN OrderItems oi ON oi.OrderId = o.Id
LEFT JOIN OrderItemDimensions d ON d.OrderItemId = oi.Id
WHERE o.Id = @orderId
ORDER BY o.Id, li.Id, d.Id;*

A consulta será realizada com sucesso obviamente. No entanto, nessa situação, estava me deparando com um problema que se refere ao Cartesian Explosion, saiba mais . Ele acontece por causa da junção da tabela OrderItemDimensions. Concluí que esta é a causa das falhas na Query, sob o tempo limite.

Revolveu?

Com o lançamento do EF Core 5.0, há um novo recurso chamado Query splitting, que permite especificar que uma consulta LINQ deve ser dividida em múltiplas consultas SQL.

Como utilizar a divisão de consulta? Basta chamar o método AsSplitQuery:

DataContext
  .Orders
  .Include(o => o.OrderItems)
  .ThenInclude(oi => oi.Dimensions)
  .AsSplitQuery()
  .First(o => o.Id == orderId); 

EF vai gerar as seguintes consultas SQL:

SELECT o.
FROM Orders o
WHERE o.Id = @orderId;

SELECT oi.*
FROM OrderItems oi
JOIN Orders o ON oi.OrderId = o.Id
WHERE o.Id = @orderId;

SELECT d.*
FROM OrderItemDimensions d
JOIN OrderItems oi ON d.OrderItemId = oi.Id
JOIN Orders o ON oi.OrderId = o.Id
WHERE o.Id = @orderId; *


Observe que para cada instrução Include temos uma consulta SQL separada. O benefício aqui é que evitamos duplicações de dados ao fazer uma determinada busca ao banco de dados.

Utilizando divisão em todas as consultas

Você pode habilitar o query splitting direto no contexto do banco de dados. Para realizar a configuração do contexto do banco de dados, você precisa chamar o método UseQuerySplittingBehavior:

services.AddDbContext<ApplicationDbContext>(
  options => options.UseSqlServer(
      "CONNECTION_STRING",
      o => o.UseQuerySplittingBehavior(
          QuerySplittingBehavior.SplitQuery))); 

Logo, todas as consultas geradas pelo EF serão consultas divididas e para reverter numa única consulta, basta chamar o método AsSingleQuery:

dataContext
  .Orders
  .Include(o => o.OrderItems)
  .ThenInclude(oi => oi.Dimensions)
  .AsSingleQuery()
  .First(o => o.Id == orderId); 

Tudo que você precisa saber sobre o Query Splitting

Mesmo que seja uma solução excelente do EF Core, algumas coisas que você precisa saber:

  1. Não há garantia de consistência nas consultas. Então o importante é ficar atento se há alguma atualização simultânea;
  2. Com as viagens mediante as querys, cuidado com o desempenho devido a alta latência!!

https://github.com/William-io

Compartilhe
Comentários (1)
Renato Olmedo
Renato Olmedo - 09/02/2023 23:21

Muito interessante! Uso EF Core e não sabia disso