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:


Comments (2)

  1. Fala Catae,

    Show de bola esses posts sobre EF, ficarei no aguardo dos demais.

    Ultimamente tenho pegado umas querys bizarras do Entity Framework, que dificultam muito o processo de tuning, pois normalmente elas são geradas pelo EF, estou tentando também entender alguns aspectos do EF, eu já usei bastante LINQ agora estou querendo começar um projeto pessoal utilizando apensa EF, mas preciso entender essas esquisitisses, aguardarei os novos posts.

    Reginaldo Silva

    1. Oi Reginaldo,
      Essa sequencia de artigos é uma visão bem negativa do Entity Framework (EF), pois tivemos bastante problemas de desempenho com o EF e só descobrimos quando olhamos o código SQL. Pretendo retomar o assunto com as vantagens do EF no futuro, mas eu adianto que prefiro trabalhar com Data-First ou MicroORM (Dapper).
      Abraços, Fabricio

Skip to main content