Jak wykonać dołączenie do grupy w .NET Core 3.0 Entity Framework?

13

Wraz ze zmianami w .NET Core 3.0 otrzymuję

... NavigationExpandingExpressionVisitor 'nie powiodło się. Może to wskazywać na błąd lub ograniczenie w EF Core. Bardziej szczegółowe informacje można znaleźć na https://go.microsoft.com/fwlink/?linkid=2101433 .) ---> System.InvalidOperationException: Przetwarzanie wyrażenia LINQ „GroupJoin, ...

To jest naprawdę proste zapytanie, więc musi istnieć sposób na wykonanie go w .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();

Zasadniczo szukam zapytania Linq (lub składni metody jak wyżej), które połączy Studia nad Pacjentami i ustawi Studia na pustą listę lub zero, jeśli nie ma badań dla danego pacjenta.

Jakieś pomysły? Działa to w .NET Core 2.2. Również powyższy link MSFT wspomina, że ​​zmiana podziału klucza jest związana z oceną po stronie klienta i unikaniem, aby wygenerowane zapytanie odczytywało całe tabele, które następnie należy połączyć lub przefiltrować po stronie klienta. Jednak przy tym prostym zapytaniu połączenie powinno być łatwe do wykonania po stronie serwera.

shelbypereira
źródło

Odpowiedzi:

11

Jak omówiono tutaj , próbujesz zapytania, które nie jest obsługiwane przez bazę danych. EF Core 2 użył oceny po stronie klienta, aby twój kod działał, ale EF Core 3 odmawia, ponieważ wygoda po stronie klienta wiąże się z kosztem trudnych do debugowania problemów z wydajnością wraz ze wzrostem zestawu danych.

Możesz użyć przycisku, DefaultIfEmptyaby wyjść, dołączyć do badań pacjentów, a następnie grupować ręcznie za pomocą 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

Powyższy przykład pobiera pełne elementy Pacjent i Badanie, ale zamiast tego możesz wybrać kolumny. Jeśli dane, których potrzebujesz od pacjenta, są zbyt duże, aby je powtórzyć dla każdego badania, w połączonym zapytaniu wybierz tylko identyfikator pacjenta, sprawdzając pozostałe dane pacjenta w oddzielnym, niepołączonym zapytaniu.

Edward Brey
źródło
2
Odpowiedź działa! Myślę, że w tłumaczeniu zapytań jest jeszcze trochę do zrobienia. Takie proste zapytanie powinno być przetłumaczalne. Nie powinno być problemów z wydajnością prostego łączenia grup dwóch tabel, ponieważ zbiór danych rośnie, zakładając, że FK / indeksy są poprawne. Podejrzewam, że wiele osób będzie miało ten problem, dołączenie do 2 tabel jest dość standardowym i często używanym zapytaniem.
shelbypereira
@ she72 Zgadzam się. Wygląda na to, że problem wynika z różnicy w sposobie używania słowa kluczowego „group” przez LINQ i SQL. EF Core powinien przekształcić LINQ groupbyw lewe złączenia, w których nie spowoduje to cofnięcia większej liczby wierszy niż oczekiwano. Odpowiednio opublikowałem komentarz .
Edward Brey,
Mam pytanie uzupełniające, wciąż próbuję zrozumieć, dlaczego grupowanie tego typu zapytań musi odbywać się po stronie klienta, wydaje się ograniczeniem nowej struktury LINQ. W powyższym przypadku nie widzę żadnego ryzyka spowalniającego wykonanie po stronie klienta w nieoczekiwany sposób. Możesz wyjaśnić?
shelbypereira
1
A w dalszej kolejności głównym problemem jest: w przeformułowanym zapytaniu, która grupa po stronie klienta, jeśli mam 1000 badań na pacjenta, będę ładować każdego pacjenta 1000 razy z bazy danych? czy istnieje jakaś alternatywa, aby zmusić tę pracę do wykonania w bazie danych i zwrócić zgrupowane wyniki?
shelbypereira
1
@ shev72 Jedyna grupa, którą rozumie baza danych, obejmuje agregaty, na przykład zapytanie pacjentów z liczbą badań na pacjenta. Baza danych zawsze zwraca prostokątny zestaw danych. Klient musi utworzyć hierarchiczną grupę. Można na to spojrzeć jako ocenę po stronie klienta lub jako część ORM . W grupie hierarchicznej dane encji nadrzędnej są powtarzane, ale nie są ponownie pytane.
Edward Brey,
0

Miałem dokładnie ten sam problem i wielką walkę. Okazuje się, że .net Core 3.0 nie obsługuje Join lub Groupjoin w składni metod (jeszcze?). Zabawne jest to, że działa w składni zapytania.

Spróbuj, to składnia zapytania z odrobiną metody. To przekłada się ładnie na prawidłowe zapytanie SQL z ładnym lewym złączeniem zewnętrznym i jest przetwarzane w bazie danych. Nie mam twoich modeli, więc musisz sam sprawdzić składnię ...

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
źródło
Nawiasem mówiąc, Join i GroupJoin za pomocą metody syntac DO działają z non-core Framework i EF. I tłumacz na właściwe zapytanie, które jest pobierane po stronie serwera
hwmaat
1
co to są badania w badaniach. Wybierz (s1 => s1)
Ankur Arora,
Modele nie zostały uwzględnione w pytaniu, więc nie znam modelu badań. Domyślam się, że jest to wirtualna kolekcja w modelu.
hwmaat