Como executar uma junção de grupo no .NET Core 3.0 Entity Framework?

13

Com as alterações no .NET Core 3.0, estou recebendo

... NavigationExpandingExpressionVisitor 'falhou. Isso pode indicar um bug ou uma limitação no EF Core. Consulte https://go.microsoft.com/fwlink/?linkid=2101433 para obter informações mais detalhadas.) ---> System.InvalidOperationException: Processando a expressão LINQ 'GroupJoin, ...

Esta é uma consulta realmente simples, portanto, deve haver uma maneira de realizá-la no .NET CORE 3.0:

 var queryResults1 = await patients
            .GroupJoin(
                _context.Studies,
                p => p.Id,
                s => s.Patient.Id,
                (p, studies) => new 
                {
                    p.DateOfBirth,
                    p.Id,
                    p.Name,
                    p.Sex,
                   Studies =studies.Select(s1=>s1)
                }
            )
            .AsNoTracking().ToListAsync();

Basicamente, estou procurando uma consulta Linq (ou sintaxe do método acima) que junte Estudos aos Pacientes e defina Estudos para uma lista vazia ou nula se não houver estudos para o paciente em questão.

Alguma ideia? Isso estava funcionando no .NET Core 2.2. Além disso, o link MSFT acima menciona que a alteração de quebra de chave está relacionada à avaliação do lado do cliente e evitando que a consulta gerada leia tabelas inteiras que devem ser unidas ou filtradas no lado do cliente. No entanto, com essa consulta simples, a associação deve ser facilmente executável no lado do servidor.

shelbypereira
fonte

Respostas:

11

Conforme discutido aqui , você está tentando uma consulta que não é suportada pelo banco de dados. O EF Core 2 usou a avaliação do lado do cliente para fazer seu código funcionar, mas o EF Core 3 recusa, porque a conveniência do lado do cliente tem o custo de problemas de desempenho difíceis de depurar à medida que o conjunto de dados aumenta.

Você pode usar use DefaultIfEmptypara ingressar à esquerda nos estudos dos pacientes e depois agrupá-los manualmente ToLookup.

var query =
    from p in db.Patients
    join s in db.Studies on p.Id equals s.PatientId into studies
    from s in studies.DefaultIfEmpty()
    select new { Patient = p, Study = s };

var grouping = query.ToLookup(e => e.Patient); // Grouping done client side

O exemplo acima captura todas as entidades Paciente e Estudo, mas você pode escolher as colunas. Se os dados que você precisa do Paciente são muito grandes para serem repetidos para cada Estudo, na consulta unida, selecione apenas a ID do Paciente, consultando o restante dos dados do Paciente em uma consulta não unida separada.

Edward Brey
fonte
2
Resposta funciona! Acho que ainda há trabalho a ser feito no tradutor de consultas. Uma consulta simples como essa deve ser traduzível. Não deve haver problemas de desempenho para uma junção de grupo simples de 2 tabelas, pois o conjunto de dados aumenta assumindo que FK / índices estão corretos. Eu suspeito que muitas pessoas terão esse problema, uma junção de grupo de 2 tabelas é uma consulta bastante padrão e frequentemente usada.
Shelbypereira 03/11/19
@ she72 eu concordo. Parece que o problema decorre da diferença de como o LINQ e o SQL usam a palavra-chave "group". O EF Core deve converter o LINQ groupbyem junções esquerdas onde isso não retira mais linhas do que o esperado. Eu postei um comentário em conformidade.
Edward Brey
Tenho uma pergunta de acompanhamento, ainda estou tentando entender por que o agrupamento para esse tipo de consulta precisa ser feito no lado do cliente, parece uma limitação da nova estrutura LINQ. Para o caso acima, não vejo riscos de diminuir a execução do lado do cliente de maneiras inesperadas. Você pode esclarecer?
Shelbypereira 07/11/19
11
E como acompanhamento adicional, a principal preocupação é: em sua consulta reformulada que agrupa o lado do cliente, se eu tiver 1000 estudos por paciente, carregarei cada paciente 1000 vezes a partir do DB? existe alguma alternativa para forçar esse trabalho a ser realizado no banco de dados e retornar os resultados agrupados?
Shelbypereira 7/11
11
@ shev72 O único agrupamento que o banco de dados compreende envolve agregados, por exemplo, uma consulta de pacientes com uma contagem de estudos por paciente. O banco de dados sempre retorna um conjunto de dados retangular. Um agrupamento hierárquico deve ser composto pelo cliente. Você pode considerar isso como avaliação do lado do cliente ou como parte do ORM . Em um agrupamento hierárquico, os dados da entidade pai são repetidos, embora não sejam consultados novamente.
Edward Brey
0

Tinha exatamente o mesmo problema e uma grande luta com ele. Acontece que o .net Core 3.0 não suporta Join ou Groupjoin na sintaxe do método (ainda?). A parte divertida é que ele funciona na sintaxe de consulta.

Tente isso, é sintaxe de consulta com um pouco de sintaxe de método. Isso se traduz muito bem na consulta SQL correta com uma boa junção externa esquerda e é processada no banco de dados. Eu não tenho seus modelos, então você precisa verificar a sintaxe ...

var queryResults1 = 
    (from p in _context.patients
    from s in _context.Studies.Where(st => st.PatientId == p.Id).DefaultIfEmpty()
    select new
    {
        p.DateOfBirth,
        p.Id,
        p.Name,
        p.Sex,
        Studies = studies.Select(s1 => s1)
    }).ToListAsync();
hwmaat
fonte
A propósito, o Join e o GroupJoin with Method syntac DO funcionam com o Framework e o EF não essenciais. E traduza para a consulta correta que está no lado do servidor
processado
11
o que é estudos em studies.Select (S1 => S1)
Ankur Arora
Os modelos não foram incluídos na pergunta, portanto não conheço o modelo de estudos. Meu melhor palpite é que essa é uma coleção virtual no modelo.
precisa saber é