Wie führe ich einen Gruppenbeitritt in .NET Core 3.0 Entity Framework durch?

13

Mit den Änderungen an .NET Core 3.0 bekomme ich

... NavigationExpandingExpressionVisitor 'fehlgeschlagen. Dies kann entweder auf einen Fehler oder eine Einschränkung in EF Core hinweisen. Weitere Informationen finden Sie unter https://go.microsoft.com/fwlink/?linkid=2101433 .) ---> System.InvalidOperationException: Verarbeitung des LINQ-Ausdrucks 'GroupJoin, ...

Dies ist eine wirklich einfache Abfrage, daher muss es eine Möglichkeit geben, sie in .NET CORE 3.0 auszuführen:

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

Ich suche im Grunde nach einer Linq-Abfrage (oder Methodensyntax wie oben), die Studien zu Patienten verknüpft und Studien auf eine leere Liste oder null setzt, wenn für den angegebenen Patienten keine Studien vorhanden sind.

Irgendwelche Ideen? Dies funktionierte in .NET Core 2.2. Der obige MSFT-Link erwähnt auch, dass die Änderung des Schlüsselbruchs mit der clientseitigen Auswertung zusammenhängt und vermieden wird, dass die generierte Abfrage ganze Tabellen liest, die dann clientseitig verbunden oder gefiltert werden müssen. Bei dieser einfachen Abfrage sollte der Join jedoch serverseitig leicht zu erledigen sein.

Shelbypereira
quelle

Antworten:

11

Wie hier erläutert , versuchen Sie eine Abfrage, die von der Datenbank nicht unterstützt wird. EF Core 2 verwendete die clientseitige Auswertung, damit Ihr Code funktioniert. EF Core 3 lehnt dies jedoch ab, da der clientseitige Komfort mit zunehmendem Dataset auf Kosten schwer zu debuggender Leistungsprobleme geht.

Sie können verwenden, DefaultIfEmptyum links an den Patientenstudien teilzunehmen und dann manuell mit zu gruppieren 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

Im obigen Beispiel werden die vollständigen Entitäten "Patient" und "Studie" erfasst. Sie können jedoch stattdessen Spalten auswählen. Wenn die Daten, die Sie vom Patienten benötigen, zu groß sind, um sie für jede Studie zu wiederholen, wählen Sie in der verknüpften Abfrage nur die Patienten-ID aus und fragen Sie den Rest der Patientendaten in einer separaten nicht verknüpften Abfrage ab.

Edward Brey
quelle
2
Antwort funktioniert! Ich denke, es gibt noch einiges zu tun im Abfrageübersetzer. Eine einfache Abfrage wie diese sollte übersetzbar sein. Bei einer einfachen Gruppenverknüpfung von 2 Tabellen sollten keine Leistungsprobleme auftreten, da der Datensatz unter der Annahme zunimmt, dass FK / Indizes korrekt sind. Ich vermute, dass viele Leute dieses Problem haben werden. Ein 2-Tabellen-Gruppen-Join ist eine Standard- und häufig verwendete Abfrage.
Shelbypereira
@ she72 Ich stimme zu. Es sieht so aus, als ob das Problem auf den Unterschied zurückzuführen ist, wie LINQ und SQL das Schlüsselwort "group" verwenden. EF Core sollte den LINQ groupbyin linke Verknüpfungen übersetzen, bei denen dadurch nicht mehr Zeilen als erwartet zurückgezogen werden. Ich habe einen entsprechenden Kommentar gepostet .
Edward Brey
Ich habe eine Folgefrage, ich versuche immer noch zu verstehen, warum die Gruppierung für diese Art von Abfrage clientseitig erfolgen muss, scheint eine Einschränkung des neuen LINQ-Frameworks zu sein. Für den obigen Fall sehe ich kein Risiko, dass es die clientseitige Ausführung auf unerwartete Weise verlangsamt. Könntest Du das erläutern?
Shelbypereira
1
Und als weiteres Follow-up ist das Hauptanliegen: In Ihrer neu formulierten Abfrage, welche Gruppe clientseitig ist, wenn ich 1000 Studien pro Patient habe, lade ich jeden Patienten 1000 Mal aus der DB? Gibt es eine Alternative, um diese Arbeit in der Datenbank zu erzwingen und die gruppierten Ergebnisse zurückzugeben?
Shelbypereira
1
@ shev72 Die einzige Gruppierung, die die Datenbank versteht, umfasst Aggregate, beispielsweise eine Abfrage von Patienten mit einer Anzahl von Studien pro Patient. Die Datenbank gibt immer einen rechteckigen Datensatz zurück. Eine hierarchische Gruppierung muss vom Kunden erstellt werden. Sie können es als clientseitige Bewertung oder als Teil des ORM betrachten . In einer hierarchischen Gruppierung werden übergeordnete Entitätsdaten wiederholt, jedoch nicht erneut abgefragt.
Edward Brey
0

Hatte genau das gleiche Problem und einen großen Kampf damit. Es stellt sich heraus, dass .net Core 3.0 Join oder Groupjoin in der Methodensyntax (noch?) Nicht unterstützt. Der lustige Teil ist jedoch, dass es in der Abfragesyntax funktioniert.

Versuchen Sie dies, es ist Abfragesyntax mit ein bisschen Methodensyntax. Dies führt zu einer korrekten SQL-Abfrage mit einem schönen linken äußeren Join und wird in der Datenbank verarbeitet. Ich habe deine Modelle nicht, also musst du die Syntax selbst überprüfen ...

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
quelle
Übrigens funktionieren die Syntax Join und GroupJoin with Method mit dem Nicht-Core-Framework und EF. Und übersetzen Sie in die richtige Abfrage, die serverseitig ist
hwmaat
1
Was ist Studien in Studien. Wählen Sie (s1 => s1)
Ankur Arora
Die Modelle wurden nicht in die Frage aufgenommen, daher kenne ich das Studienmodell nicht. Ich vermute, dass dies eine virtuelle Sammlung im Modell ist.
Hwmaat