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

  • Anonymous
    July 11, 2017
    The comment has been removed
    • Anonymous
      July 11, 2017
      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
  • Anonymous
    October 24, 2017
    Ola eu to Começando a aprender C# e to com um problema para realiza uma consulta eu preciso agrupar por data mais no to conseguindo fazer sera que alguém poderia me ajuda a resolver este problema a baixo esta o meu código fonte:public void BuscarVendaDeProdurtos_Saidas() { var data1 = Convert.ToDateTime(dateINI_Saida.Text); var data2 = Convert.ToDateTime(dateFINI_Saida.Text); var cod = Convert.ToInt64(textBox8.Text); var pesquisa = from p in dc.ITEMVENDA orderby p.CD_PRODUTO descending where p.CD_PRODUTO == cod && p.Data_Venda >= data1 && p.Data_Venda <= data2 select new { p.NM_PROD, p.Data_Venda, p.QT_PRODV, }; dataGridView2.DataSource = pesquisa.ToList(); Soma = 0; foreach (DataGridViewRow dr in dataGridView2.Rows) Soma += Convert.ToDecimal(dr.Cells[2].Value); textBox12.Text = Convert.ToString(string.Format("{0:n}", Soma));essa pesquisa me retara isso:20/10/2017, Data Produto Qt20/10/2017................... 320/10/2017 ................. 1e eu preciso de algo assim :Data Produto Qt20/10/2017................... 4uma unica data já com as suas quantidades somada desde já agradeço