ho bisogno di fare un po 'di filtraggio su un ObjectSet per ottenere le entità di cui ho bisogno in questo modo:dinamicamente aggiungere nuove espressioni lambda per creare un filtro
query = this.ObjectSet.Where(x => x.TypeId == 3); // this is just an example;
Più avanti nel codice (e prima di lanciare l'esecuzione differita) Filtro la query nuovamente in questo modo:
query = query.Where(<another lambda here ...>);
Che funziona abbastanza bene finora.
Ecco il mio problema:
Le entità contiene un DateFrom proprietà e un DateTo proprietà , che sono entrambi i tipi DataTime. Rappresentano un periodo di tempo .
ho bisogno di filtrare le entità per ottenere solo quelli che fanno parte di una collezione diperiodi di tempo. I periodi della collezione non sono necessariamente contigui, quindi, la logica per retreive le entità che si presenta come:
entities.Where(x => x.DateFrom >= Period1.DateFrom and x.DateTo <= Period1.DateTo)
||
entities.Where(x => x.DateFrom >= Period2.DateFrom and x.DateTo <= Period2.DateTo)
||
... e così via per tutti i periodi della collezione.
ho provato a farlo:
foreach (var ratePeriod in ratePeriods)
{
var period = ratePeriod;
query = query.Where(de =>
de.Date >= period.DateFrom && de.Date <= period.DateTo);
}
Ma una volta che lancio l'esecuzione differita, si traduce questo in SQL proprio come lo voglio (un filtro per ciascuno dei periodi di tempo a molti periodi lì è nella raccolta), MA, si traduce in AND confronti invece di O confronti, che non restituisce nessuna entità, poiché un'entità non può essere parte di più di un periodo di tempo, ovviamente.
Ho bisogno di costruire una sorta di linq dinamico qui per aggregare i filtri del periodo.
Aggiornamento
Sulla base di risposta di Hatten, ho aggiunto il seguente membro:
private Expression<Func<T, bool>> CombineWithOr<T>(Expression<Func<T, bool>> firstExpression, Expression<Func<T, bool>> secondExpression)
{
// Create a parameter to use for both of the expression bodies.
var parameter = Expression.Parameter(typeof(T), "x");
// Invoke each expression with the new parameter, and combine the expression bodies with OR.
var resultBody = Expression.Or(Expression.Invoke(firstExpression, parameter), Expression.Invoke(secondExpression, parameter));
// Combine the parameter with the resulting expression body to create a new lambda expression.
return Expression.Lambda<Func<T, bool>>(resultBody, parameter);
}
dichiarato una nuova espressione CombineWithOr:
Expression<Func<DocumentEntry, bool>> resultExpression = n => false;
e lo ha utilizzato nel mio periodo di raccolta iterazione come questa:
foreach (var ratePeriod in ratePeriods)
{
var period = ratePeriod;
Expression<Func<DocumentEntry, bool>> expression = de => de.Date >= period.DateFrom && de.Date <= period.DateTo;
resultExpression = this.CombineWithOr(resultExpression, expression);
}
var documentEntries = query.Where(resultExpression.Compile()).ToList();
Ho esaminato l'SQL risultante ed è come se l'espressione non abbia alcun effetto. L'SQL risultante restituisce i filtri precedentemente programmati ma non i filtri combinati. Perché ?
Update 2
ho voluto dare il suggerimento di feO2x una prova, così ho riscritto la mia domanda filtro in questo modo:
query = query.AsEnumerable()
.Where(de => ratePeriods
.Any(rp => rp.DateFrom <= de.Date && rp.DateTo >= de.Date))
Come potete vedere, ho aggiunto AsEnumerable()
ma il compilatore mi ha dato un errore che non è in grado di convertire IEnumerable in IQueryable, quindi ho aggiunto ToQueryable()
alla fine della mia query:
query = query.AsEnumerable()
.Where(de => ratePeriods
.Any(rp => rp.DateFrom <= de.Date && rp.DateTo >= de.Date))
.ToQueryable();
Tutto funziona correttamente. Posso compilare il codice e avviare questa query. Tuttavia, non si adatta alle mie esigenze.
Durante la creazione del profilo SQL risultante, posso vedere che il filtro non fa parte della query SQL perché filtra le date in memoria durante il processo. Immagino che tu lo sappia già e questo è ciò che intendevi suggerire.
tuo suggerimento funziona, ma, dal momento che recupera tutte le entità dal database (e ci sono migliaia e migliaia di loro) prima di filtraggio in-memory, è davvero lento di tornare quella quantità enorme dal database .
Quello che voglio veramente è inviare il filtro dei periodi come parte della query SQL risultante, quindi non restituirà una quantità enorme di entità prima di finire con il processo di filtraggio.
Perché stai facendo 'query = query.AsEnumerable()'? Ecco perché la query non fa parte della query SQL? Non capisco il ragionamento alla base della conversione in enumerabile, e quindi di tornare a queryable. – hattenn
È stato uno dei molti modi in cui ho cercato di rendere comprensibile la query dal provider Linq-To-Entities. Sfortunatamente, l'omissione della chiamata AsEnumerable() rende la query inutilizzabile in un contesto Linq-To-Entities. Ottengo questo tipo di esecuzione: il tipo di nodo di espressione LINQ "Invoke" non è supportato in LINQ alle entità. –