5

ho bisogno di filtrare un elenco di documenti inviandoli ad un filtro personalizzato che sto lottando per creare dinamicamente utilizzando un foreach ciclo:Costruire un predicato personalizzato di agire come un filtro con un foreach ciclo

var mainPredicate = PredicateBuilder.True<Document>(); 

// mainPredicate is combined to other filters successfully here ... 

var innerPredicate = PredicateBuilder.False<Document>(); 
foreach (var period in periods) 
{ 
    var p = period; 
    Expression<Func<Document, bool>> inPeriod = 
     d => d.Date >= p.DateFrom && d.Date <= p.DateTo; 

    innerPredicate = innerPredicate.Or(d => inPeriod.Invoke(d)); 
} 

mainPredicate = mainPredicate.And(innerPredicate); 

questa ultima riga:

documents = this.ObjectSet.AsExpandable().Where(mainPredicate).ToList(); 

genera questa eccezione:

Il parametro 'd' non è stato associato nell'espressione query LINQ to Entities .

Qualcuno sa perché sto ottenendo questa eccezione? Non capisco dove si perde il parametro 'd' che sto passando al metodo InPeriod. Non so cosa manca per far funzionare tutto questo. Il mio codice è lo stesso di molti altri esempi che funzionano perfettamente. Qualsiasi ulteriore informazione teorica teorica su espressioni che invocano e come funziona dietro le quinte sono le benvenute.

+0

Cosa succede se si esegue 'mainPredicate.Expand(). Compile(). Invoke (someDocument)'? Sei sicuro che il problema non sia da qualche altra parte (il tuo codice sembra a posto per me)? – svick

+0

Dato il tuo suggerimento, ho modificato l'ultima riga in modo che assomigli a questo: 'documentEntries = this.ObjectSet.AsExpandable(). Where (de => mainPredicate.Expand(). Compile(). Invoke (de)). ToList (); 'but but: LINQ to Entities non riconosce il metodo 'Boolean Invoke (Remabec.PM.Tarification.DocumentEntry)' e questo metodo non può essere tradotto in un'espressione di archivio. –

+0

Sì, non funzionerà e non è quello che intendevo. Potresti provare ad eseguire il mio codice da solo? – svick

risposta

1

Infine, ho trovato un modo per evitare la combinazione di molteplici predicati all'espressione principale albero.

Dato che ogni predicato rappresenta un filtro diverso e voglio il, filtro combinato finale sia una serie di deve-essere rispettato condizioni, possiamo dire che ciascuno dei predicati deve tornare vero per la predicato finale per restituire vero.

Affinché funzioni, i predicati devono essere combinati con AND. Quindi, la query SQL risultante deve apparire così:

predicate1 AND predicate2 AND predicate3 ...

Un modo migliore per combinare questi predicati con AND è quello di catena Where operatori di query alla query finale, come questo:

var documents = this.ObjectSet.AsExpandable() 
    .Where(mainPredicate) 
    .Where(otherPredicate) 
    .Where(yetAnotherPredicate) 
    .ToList(); 

La query SQL risultante combina ciascuno di questi predicati con AND. Questo è proprio quello che volevo fare.

È più semplice che scavalcare un albero di espressioni da solo.

2

Non capisco il motivo per cui si esegue questa operazione:

innerPredicate = innerPredicate.Or(d => inPeriod.Invoke(d)); 

Quando si potrebbe semplicemente evitare il Invoke completamente, come questo:

innerPredicate = innerPredicate.Or(inPeriod); 

Questo dovrebbe funzionare perfettamente bene.


BTW, ho la sensazione che ci sia un bug con LINQKit qui (a meno che non ci sia una certa documentazione che suggerisce che non supporta questo scenario).

Quando ho provato questo codice simile:

Expression<Func<int, bool>> first = p1 => p1 > 4; 
Expression<Func<int, bool>> second = p2 => p2 < 2; 

// Expand is similar to AsExpandable, except it works on 
// expressions, not queryables. 
var composite = first.Or(d => second.Invoke(d)) 
        .Expand(); 

... LINQKit generato il seguente espressione composita:

p1 => ((p1 > 4) OrElse (d < 2)) // what on earth is d? 

... che ha infatti il ​​parametro non associato d (NodeType = Parameter, Name = 'd').

Evitando la Invoke con first.Or(second).Expand() genera il perfettamente ragionevole:

p1 => ((p1 > 4) OrElse (p1 < 2)) // much better now... 
+0

'innerPredicate.Or (inPeriod.Invoke)' non verrà compilato. Penso che intendessi 'innerPredicate.Or (inPeriod)'. – svick

+0

@svick: Oh, errore di battitura! Ho detto di evitare "Invoke", quindi non l'ho cancellato dalla copia-pasta. Grazie. – Ani

+0

Se non invoco esplicitamente l'espressione (come hai suggerito: 'innerPredicate.Or (inPeriod))' Ricevo: __Il parametro 'f' non è stato associato nell'espressione di query LINQ to Entities specificata .__ –

0

utilizzo questi metodi di estensione:

public static class Extensions 
{ 
    public static Expression<Func<T, bool>> OrElse<T>(this Expression<Func<T, bool>> source, Expression<Func<T, bool>> predicate) 
    { 
     InvocationExpression invokedExpression = Expression.Invoke(predicate, source.Parameters.Cast<Expression>()); 
     return Expression.Lambda<Func<T, bool>>(Expression.OrElse(source.Body, invokedExpression), source.Parameters); 
    } 

    public static Expression<Func<T, bool>> AndAlso<T>(this Expression<Func<T, bool>> source, Expression<Func<T, bool>> predicate) 
    { 
     InvocationExpression invokedExpression = Expression.Invoke(predicate, source.Parameters.Cast<Expression>()); 
     return Expression.Lambda<Func<T, bool>>(Expression.AndAlso(source.Body, invokedExpression), source.Parameters); 
    } 

} 
Problemi correlati