Come eseguire un join di gruppo in .NET Core 3.0 Entity Framework?


13

Con le modifiche a .NET Core 3.0 sto ottenendo

... NavigationExpandingExpressionVisitor 'non riuscito. Ciò può indicare un bug o una limitazione in EF Core. Vedere https://go.microsoft.com/fwlink/?linkid=2101433 per informazioni più dettagliate.) ---> System.InvalidOperationException: elaborazione dell'espressione LINQ 'GroupJoin, ...

Questa è una query davvero semplice, quindi ci deve essere un modo per eseguirla in .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();

Sto fondamentalmente cercando una query Linq (o sintassi del metodo come sopra) che unisca gli Studi ai pazienti e imposti gli Studi su un elenco vuoto o nullo se non ci sono studi per il dato paziente.

Qualche idea? Funzionava in .NET Core 2.2. Inoltre, il link MSFT sopra menziona che la modifica della rottura delle chiavi è correlata alla valutazione lato client ed evita che la query generata legga intere tabelle che devono quindi essere unite o filtrate lato client. Tuttavia, con questa semplice query, il join dovrebbe essere facilmente eseguibile sul lato server.

Risposte:


11

Come discusso qui , stai tentando una query che non è supportata dal database. EF Core 2 ha utilizzato la valutazione lato client per far funzionare il codice, ma EF Core 3 rifiuta, poiché la praticità sul lato client comporta il costo di problemi di prestazioni difficili da debug con l'aumentare del set di dati.

È possibile utilizzare use DefaultIfEmptyper unire gli studi dei pazienti a sinistra e quindi raggrupparli 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

L'esempio sopra mostra tutte le entità paziente e di studio, ma puoi invece selezionare le colonne. Se i dati necessari per il paziente sono troppo grandi per essere ripetuti per ogni studio, nella query unita selezionare solo l'ID paziente, interrogando il resto dei dati paziente in una query separata non unita.


2
La risposta funziona! Immagino che ci sia ancora del lavoro da fare nel traduttore di query. Una query semplice come questa dovrebbe essere traducibile. Non dovrebbero esserci problemi di prestazioni per un semplice join di gruppo di 2 tabelle poiché l'aumento del set di dati presuppone che FK / indici siano corretti. Ho il sospetto che molte persone avranno questo problema, un join di 2 gruppi di tabelle è una query piuttosto standard e spesso utilizzata.
shelbypereira,

@ she72 Sono d'accordo. Sembra che il problema derivi dalla differenza nel modo in cui LINQ e SQL utilizzano la parola chiave "gruppo". EF Core dovrebbe tradurre LINQ groupbyin join di sinistra, in questo modo non si ritira più righe del previsto. Ho pubblicato un commento di conseguenza.
Edward Brey,

Ho una domanda di follow-up, sto ancora cercando di capire perché il raggruppamento per questo tipo di query deve essere eseguito sul lato client, sembra una limitazione del nuovo framework LINQ. Nel caso precedente non vedo alcun rischio che rallenti l'esecuzione sul lato client in modi imprevisti. Puoi chiarire?
shelbypereira,

1
E come ulteriore follow-up la preoccupazione principale è: nella tua domanda riformulata che raggruppa il lato client se avrò 1000 studi per paziente, caricherò ogni paziente 1000 volte dal DB? c'è qualche alternativa per forzare questo lavoro da svolgere nel DB e restituire i risultati raggruppati?
shelbypereira,

1
@ shev72 L'unico raggruppamento che il database comprende comprende gli aggregati, ad esempio una query di pazienti con un conteggio di studi per paziente. Il database restituisce sempre un set di dati rettangolare. Un raggruppamento gerarchico deve essere composto dal cliente. Potresti vederlo come una valutazione lato client o come parte dell'ORM . In un raggruppamento gerarchico, i dati dell'entità padre vengono ripetuti, sebbene non vengano richiesti.
Edward Brey,

0

Aveva esattamente lo stesso problema e una grande lotta con esso. Si scopre che .net Core 3.0 non supporta Join o Groupjoin nella sintassi del metodo (ancora?). La parte divertente è però, funziona nella sintassi della query.

Prova questo, è la sintassi della query con un po 'di sintassi del metodo. Questo si traduce piacevolmente nella query SQL corretta con un bel join esterno sinistro ed è elaborato sul database. Non ho i tuoi modelli, quindi devi controllare tu stesso la sintassi ....

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();

A proposito, Join e GroupJoin con Method syntac funzionano con Framework non-core ed EF. E traduci nella query corretta che è il lato server
servito

1
cosa sono gli studi negli studi.Seleziona (s1 => s1)
Ankur Arora il

I modelli non sono stati inclusi nella domanda, quindi non conosco il modello di studi. La mia ipotesi migliore è che questa è una raccolta virtuale nel modello.
hwmaat,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.