Impacto no desempenho do Entity Framework Core 3.0 por incluir propriedades de navegação de coleção (explosão cartesiana)

8

Estamos enfrentando um grande problema de desempenho após atualizar o EF Core 2.2 para o EF Core 3.0. Imagine um modelo de dados simples com uma única propriedade de navegação de coleção e centenas de campos (a realidade parece ainda mais sombria):

public class Item
{
  [Key]
  public int ItemID {get;set;}

  public ICollection<AddInfo> AddInfos {get;set;}
  ...  // consisting of another 100+ properties!
}

e

public class AddInfo
{
  [Key]
  public int AddInfoID {get;set;}
  public int? ItemID {get;set;}
  public string SomePayload {get;set;}
}

Durante a recuperação do item, estamos consultando da seguinte maneira:

...
var myQueryable = this._context.Items.Include(i => i.AddInfos).Where(**some filter**);
... // moar filters
var result = myQueryable.ToList();

Em frente, até este ponto.

No EF 2.2, buscar esses resultados consultáveis ​​em duas consultas separadas, uma para Iteme uma para o AddInfonível. Essas consultas geralmente buscam 10.000 itemse cerca de 250.000 AddInfos.

No EF Core 3.0, no entanto, uma única consulta está sendo gerada; a união AddInfoà esquerda à Item primeira vista parece ser a melhor opção. ItemNo entanto, nosso campo precisa ser buscado com todos os mais de 100 campos, e é por isso que não é possível projetar em uma classe menor ou em um tipo anônimo (adicionar uma chamada ao método .Select (...) -). Portanto, o conjunto de resultados possui tanta redundância (cada Itemaproximadamente 25 vezes) que a consulta em si leva muito tempo para ser executada em um tempo aceitável.

O EF-Core 3.0 oferece alguma opção que nos permita voltar ao comportamento de consulta do bom e velho EF Core 2,2 vezes sem grandes alterações em nosso modelo de dados? Já estamos lucrando com essa mudança em outras partes do aplicativo, mas não nesse cenário específico.

Muito obrigado antecipadamente!

Atualizar

Após uma investigação mais aprofundada, descobri que esse problema já foi resolvido com a Microsoft aqui e fora da caixa, parece não haver maneira de configurar a execução da consulta dividida.

TheSchmu
fonte
A consulta está em uma conexão da web (http)? Os cabeçalhos padrão podem ser diferentes no Core 2.2 e no Core 3.0 (como http versão 1.0 e http versão 1.1). Você pode usar um sniffer como wireshark ou fiddler e verificar a primeira solicitação para verificar se os cabeçalhos são os mesmos.
Jdweng #
1
Leia este aviso . Você está certo, o comportamento foi alterado, ele corresponde ao antigo comportamento do EF 6.x. O que eu não consigo entender Como o EF 2.2 foi traduzido para apenas 2 consultas. Você tem amostras para isso?
Eldar
@ Eldar, obrigado por fornecer o link. A conversão para duas consultas foi possível, porque o mesmo filtro (-> Cláusula WHERE) foi usado para buscar dados da tabela Item e AddInfo, a última, é claro, com uma junção de volta à própria tabela de itens. A EF costurou os itens e suas propriedades de navegação de coleção novamente na memória.
TheSchmu 04/04/19

Respostas:

3

De acordo com a atualização da minha pergunta inicial, os insights chegaram ao ponto de garantir a mim mesmo que, atualmente, não há realmente nenhuma configuração incorporada para retornar à execução da consulta dividida.

No entanto, a MS forneceu exemplos de código sobre como fazer isso com alterações mínimas de código (para o nosso caso de uso!) Aqui .

Estamos simplesmente removendo as chamadas .Include (...) para as propriedades de navegação da coleção (relações 1: n no nosso caso, relações 1: 1 não são afetadas!). Depois de buscar os itens, estamos simplesmente fazendo outra chamada usando:

...
var myQueryable = this._context.Items.Where(**some filter**);
... // moar filters
var result = myQueryable.ToList();
...
var addInfos = myQueryable.Include(x => x.AddInfos).SelectMany(x => x.AddInfos).Select(x => new {x.ItemID, x}).ToList();

Isso busca as entidades de propriedades de navegação da coleção e - se o rastreamento de alterações estiver ativado - preenche automaticamente as coleções nos itens individuais da resultvariável.

TheSchmu
fonte
Você também pode compartilhar a saída (consulta sql gerada) da segunda consulta.
Eldar