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.

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.


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

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.