Entity Framework: Broken LINQ

Não tenho nada contra o LINQ. Gosto da funcionalidade e acho sua sintaxe bastante expressiva:

image

Se quisesse evitar duplicação de usuários, bastaria adicionar uma chamada ao Distinct.

image

Por outro lado, a implementação procedural equivalente já não seria tão elegante.

image

Existem vários exemplos que mostram agrupamento e agregação:

LINQ query examples
https://msdn.microsoft.com/en-us/library/gg509017.aspx

Embora o LINQ seja uma funcionalidade poderosa, há várias formas que ela pode esconder um problema.

Analisando o ARDA e queries LINQ

O Dashboard inicial consulta os workloads associados a um determinado usuário, chamando o método GetWorkloadsByUser para obter as informações do banco de dados. Vamos pegar a implementação de junho/2016 do repositório Arda_old e analisar.

image

Arda_old/ARDA/Arda.Kanban/Repositories/WorkloadRepository.cs
https://github.com/DXBrazil/Arda_old/blob/04b12d051493cc1c1005745f89144c78736f463f/ARDA/Arda.Kanban/Repositories/WorkloadRepository.cs

Esse foi um dos métodos mais complexos do projeto em relação ao desempenho do Entity Framework.

Round-trips entre cliente e servidor

O trecho abaixo demorava alguns segundos para completar sua execução em ambiente de desenvolvimento e produção. Quando a latência de rede aumentava (desenvolvimento local), esse tempo chegava a minutos!

image

[GitHub Arda] Calling /api/Workload/ListWorkloadbyUser does many SQL queries (+100)
https://github.com/DXBrazil/Arda/issues/22

A causa do problema era uma grande quantidade de chamadas ao servidor de banco de dados. Na realidade, cada interação do loop corresponde a uma execução de query SQL.

Realizando JOIN entre tabelas

Há um trecho do código que faz o JOIN entre as tabelas de WorkloadBacklogs e WorkloadBacklogUsers. Inicialmente ela começou dessa forma:

image_thumb[41]_thumb[7]

Depois substituímos SingleOrDefault por Where + First:

image

Infelizmente, no upgrade para o .NET Core 1.1, esse trecho de código parou de funcionar. Começamos a receber exceções do Entity Framework, embora funcionasse normalmente na versão anterior 1.0.

image

A solução definitiva foi entender a sintaxe correta do LINQ e escrever um JOIN sobre o WBID ao invés de WBUserId. O desempenho melhorou bastante e o código ficou mais legível.

image

[GitHub Issue #22]  Kanban.ListWorkloadsByUser is broken after upgrading to NetCore 1.1
https://github.com/DXBrazil/Arda/issues/21

 

Conclusão

Entity Framework (EF) transforma a query LINQ em uma consulta T-SQL. Entretanto, não há clareza na forma com que o EF realiza isso. Por isso, é comum ver aplicações funcionando, mas com um desempenho ruim.

No próximo artigo, gostaria de abordar esse mesmo assunto sob a perspectiva do DBA.

Demais artigos: