Ich versuche eine vernünftige Abfrage in EF Core zu erstellen, die eine Sammlung von Dingen zurückgibt, die wiederum aus einer Sammlung von Dingen abgeleitet sind. Grundsätzlich würde man in Raw SQL einen JOIN machen.
Es befindet sich in ASP.NET Core. Die erste Auflistung ist also die Liste der Rollen für das SecurityPrincipal
Objekt:
var roles = User.FindAll(ClaimTypes.Role).Select(r=>r.Value);
Diese Rollen werden dann Gruppen in unserer Datenbank zugeordnet, sodass ich sie nachschlagen kann:
var groupsQuery = dbContext.Groups.Where(g=>roles.Any(r=>r==g.GroupName));
var groups = await groupsQuery.ToListAsync();
Diese Abfrage ist ziemlich zufrieden und gibt erwartungsgemäß eine Sammlung von Gruppen zurück. Die Gruppen haben jedoch Zugriff auf eine andere Ressource, was ich wirklich will und weil es eine viel zu viele Beziehung gibt, gibt es einen Brückentisch.
Ich versuche, die AssetGroup-Verknüpfungstabelle abzufragen, damit ich alle Assets abrufen kann, auf die alle Gruppen verweisen, die auf dem SecurityPrincipal
einer Rolle zugeordnet sind.
var assetGroupsQuery = dbContext.AssetsGroups.Where(ag => groupsQuery.Any(ag => ag.Id == a.GroupId));
var assetGroups = await assetGroupsQuery.ToListAsync();
Bei der zweiten Abfrage bekomme ich viel Spam in meinem Ausgabefenster:
The LINQ expression 'where ([ag].Id == [ag].GroupId)' could not be translated and will be evaluated locally.
The LINQ expression 'Any()' could not be translated and will be evaluated locally.
The LINQ expression 'where {from Group g in __groups_0 where ([ag].Id == [ag].GroupId) select [ag] => Any()}' could not be translated and will be evaluated locally.
The LINQ expression 'where ([ag].Id == [ag].GroupId)' could not be translated and will be evaluated locally.
The LINQ expression 'Any()' could not be translated and will be evaluated locally.
Gibt es Hinweise, wie eine geschachtelte Abfrage so formuliert werden sollte, damit EF Core eine einzelne SQL-Abfrage ordnungsgemäß erstellen kann?
Vermeiden Sie im Allgemeinen die Verwendung von Any
oder LINQ-Operatoren mit IEnumerable<string>
Contains
in der Speichersammlung wie Ihre roles
(die gemäß dem Code den Typ IEnumerable<string>
).
Mit anderen Worten, statt
.Where(g => roles.Any(r => r == g.GroupName))
Verwenden Sie das funktional Äquivalent
.Where(g => roles.Contains(g.GroupName))
Letzteres wird garantiert in SQL IN
, das erste jedoch nicht.
Interessant und gleichzeitig irreführend ist, dass EF Core versucht, die Formatvorlagen auf die gleiche Weise wie Contains
und erfolgreich zu sein, und zwar erfolgreich, wenn die enthaltene Abfrage ausgeführt wird, jedoch nicht, wenn sie als Teil einer anderen Abfrage verwendet wird.
Dies kann als aktueller EF Core-Implementierungsfehler betrachtet werden. Die Problemumgehung / Lösung besteht jedoch (wie zu Beginn erwähnt) darin, sich nicht darauf zu verlassen und immer Contains
.