Sto cercando di costruire una query sana in EF Core che restituisca una raccolta di elementi che, a loro volta, derivano da una raccolta di elementi. Fondamentalmente in SQL raw si farebbe un JOIN.
Si trova in ASP.NET Core, quindi la raccolta iniziale è l'elenco di ruoli nell'oggetto SecurityPrincipal
:
var roles = User.FindAll(ClaimTypes.Role).Select(r=>r.Value);
Questi ruoli vengono quindi associati ai gruppi nel nostro database, quindi posso osservarli:
var groupsQuery = dbContext.Groups.Where(g=>roles.Any(r=>r==g.GroupName));
var groups = await groupsQuery.ToListAsync();
Questa query è abbastanza felice e restituisce una raccolta di gruppi come previsto. I gruppi hanno comunque accesso a un'altra risorsa, che è ciò che voglio veramente e perché la sua relazione molte a molte c'è una tabella di bridging.
Questo è il mio tentativo di interrogare la tabella di join di AssetGroup in modo da poter ottenere tutte le risorse a cui fanno riferimento tutti i gruppi che eseguono l'associazione a un ruolo su SecurityPrincipal
.
var assetGroupsQuery = dbContext.AssetsGroups.Where(ag => groupsQuery.Any(ag => ag.Id == a.GroupId));
var assetGroups = await assetGroupsQuery.ToListAsync();
Quando eseguo la seconda query ottengo un sacco di spam nella mia finestra di output:
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.
Qualsiasi indizio su come si dovrebbe esprimere una query nidificata come questa in modo che EF Core possa comporre correttamente una singola query SQL?
In generale evitare di utilizzare Any
o qualsiasi operatore LINQ diverso da Contains
nella raccolta di memoria come i propri roles
(che secondo il codice dovrebbe essere di tipo IEnumerable<string>
).
In altre parole, invece di
.Where(g => roles.Any(r => r == g.GroupName))
usa l'equivalente funzionalmente
.Where(g => roles.Contains(g.GroupName))
Il successivo è garantito per essere tradotto in SQL IN
, mentre il primo non lo è.
È interessante notare che, allo stesso tempo, è fuorviante che EF Core cerca di essere intelligente e traduce il precedente allo stesso modo di Contains
e riesce quando viene eseguita la query contenente, ma non quando viene utilizzata come parte di un'altra query.
Potrebbe essere considerato un difetto di implementazione attuale di EF Core. Ma la soluzione / soluzione è (come detto all'inizio) per non fare affidamento su di essa e utilizzare sempre Contains
.