Entity Framework: Broken LINQ
Não tenho nada contra o LINQ. Gosto da funcionalidade e acho sua sintaxe bastante expressiva:
Se quisesse evitar duplicação de usuários, bastaria adicionar uma chamada ao Distinct.
Por outro lado, a implementação procedural equivalente já não seria tão elegante.
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.
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!
[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:
Depois substituímos SingleOrDefault por Where + First:
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.
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.
[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:
- Entity Framework: Code-First, Database-Never
- Entity Framework: Broken LINQ
- Entity Framework: A vida do DBA
- 7 Motivos para não usar o Entity Framework
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
- 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