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:
- Não há garantia de consistência nas consultas. Então o importante é ficar atento se há alguma atualização simultânea;
- Com as viagens mediante as querys, cuidado com o desempenho devido a alta latência!!
https://github.com/William-io