Ciò che sembrava essere un compito relativamente semplice si è trasformato in qualcosa di un problema sorprendentemente complesso. Al punto che sto iniziando a pensare che la mia metodologia sia forse fuori portata con le capacità di Linq.
Quello che sto cercando di fare è mettere insieme una query di Linq e quindi invocare .Include()
per ottenere valori di pull da un numero di entità figlio. Ad esempio, supponiamo di avere queste entità:
public class Parent
{
public int Id { get; set; }
public string Name { get; set; }
public string Location { get; set; }
public ISet<Child> Children { get; set; }
}
public class Child
{
public int Id { get; set; }
public int ParentId { get; set; }
public Parent Parent { get; set; }
public string Name { get; set; }
}
Diciamo che voglio eseguire una query per recuperare i record da Parent
, dove Name
è un valore e Location
è un altro valore e quindi include anche i record Child
. Ma per qualsiasi motivo non conosco i valori della query per Name
e Location
allo stesso tempo, quindi devo prendere due queryable separate e unirmi a loro, ad esempio:
MyDbContext C = new MyDbContext();
var queryOne = C.Parent.Where(p => p.Name == myName);
var queryTwo = C.Parent.Where(p => p.Location == myLocation);
var finalQuery = queryOne.Intersect(queryTwo);
Funziona bene, producendo risultati esattamente come se avessi appena fatto:
var query = C.Parent.Where(p => p.Name == myName && p.Location = myLocation);
E allo stesso modo, posso:
var finalQuery = queryOne.Union(queryTwo);
Per darmi risultati come se avessi:
var query = C.Parent.Where(p => p.Name == myName || p.Location = myLocation);
Ciò che non posso fare, tuttavia, una volta applicato l' Intersect()
o Union()
, tuttavia, viene eseguito il mapping del Child
utilizzando Include()
, come in:
finalQuery.Include(p => p.Children);
Questo codice verrà compilato, ma produce risultati come segue:
Union()
, verrà prodotto un set di risultati, ma nessuna entità Child
verrà enumerata. Intersect()
, viene generato un errore di runtime al tentativo di applicare Include()
, come segue: L'espressione di tipo "System.Collections.Generic.IEnumerable`1 [Microsoft.EntityFrameworkCore.Query.Internal.AnonymousObject]" non può essere utilizzata per il parametro di tipo "System.Collections.Generic.IEnumerable`1 [System.Object]" del metodo 'System.Collections.Generic.IEnumerable`1 [System.Object] Intersect [Object] (System.Collections.Generic.IEnumerable`1 [System.Object], System.Collections.Generic.IEnumerable`1 [System.Object]) '
La cosa che mi sconcerta è che questo codice funzionerà esattamente come previsto:
var query = C.Parent.Where(p => p.Name == myName).Where(p => p.Location == myLocation);
query.Include(p => p.Children);
Vale a dire, con i risultati desiderati, incluse le entità Child
elencate.
la mia metodologia forse è semplicemente fuori portata con le capacità di Linq
Il problema non è LINQ, ma la traduzione della query EF Core, e in particolare la mancanza della traduzione SQL del metodo Intersect
/ Union
/ Concat
/ Except
, tracciata da # 6812 Query: Translate IQueryable.Concat / Union / Intersect / Except / etc. al server .
In breve, tali query attualmente utilizzano la valutazione del cliente , che con la combinazione di come EF Core gestisce Include
porta a molte eccezioni di runtime impreviste (come il caso n. 2) o comportamenti errati (come Ignora include nel caso n. 1).
Quindi, sebbene il tuo approccio tecnicamente abbia perfettamente senso, secondo la risposta del leader del team EF Core
Cambiare questo per produrre una singola query SQL sul server non è attualmente una priorità assoluta
quindi al momento questo non è nemmeno pianificato per la versione 3.0, anche se ci sono piani per cambiare (riscrivere) l'intera pipeline di traduzione delle query, che potrebbe consentire di implementare anche quella.
Per ora non hai opzioni. Puoi provare a elaborare da solo gli alberi delle espressioni di query, ma è un'attività complicata e probabilmente scoprirai perché non è ancora implementata :) Se puoi convertire le tue query nella singola query equivalente con la condizione Where
combinata, l'applicazione di Include
sarà bene.
PS Si noti che anche ora il tuo approccio tecnicamente "funziona" senza Include
, il modo migliore in cui viene valutato il lato client lo rende assolutamente non equivalente alla singola query corrispondente.