5

muszę filtrować listę dokumentów, przekazując je do filtru niestandardowego, że jestem stara się zbudować dynamicznieużywając foreach pętlę:Budowanie niestandardowego orzecznik działać jako filtr przy użyciu foreach pętli

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); 

Ta ostatnia linia:

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

generuje ten wyjątek:

Parametr "d" nie był powiązany w określonym wyrażeniu zapytania LINQ to Entity .

Ktoś wie, dlaczego otrzymuję ten wyjątek? Nie rozumiem, gdzie tracę parametr "d", który przekazuję do metody InPeriod. Nie wiem, czego brakuje, aby to zadziałało. Mój kod jest taki sam jak wiele innych przykładów, które działają idealnie. Wszelkie mile widziane teoretyczne informacje na temat powoływania się na wyrażenia i tego, jak działa za kulisami, są mile widziane.

+0

Co się stanie, jeśli wykonasz 'mainPredicate.Expand() .Kompiluj(). Invoke (someDocument)'? Jesteś pewien, że problem nie leży gdzieś indziej (Twój kod wygląda dla mnie dobrze)? – svick

+0

Biorąc pod uwagę Twoją sugestię, zmodyfikowałem ostatnią linię tak, aby wyglądała następująco: 'documentEntries = this.ObjectSet.AsExpandable(). Gdzie (de => mainPredicate.Expand() .Kompiluj(). Invoke (de)). ToList (); 'ale rzuca: LINQ to Entities nie rozpoznaje metody 'Boolean Invoke (Remabec.PM.Tarification.DocumentEntry)' metoda, a tej metody nie można przetłumaczyć na wyrażenie store. –

+0

Tak, to nie zadziała i nie o to mi chodziło. Czy mógłbyś spróbować wykonać mój kod sam? – svick

Odpowiedz

1

Wreszcie znalazłem sposób na uniknięcie łączenia wielu predykaty do głównego wyrazu drzewo.

Biorąc pod uwagę, że każde orzeczenie reprezentuje inny filtr i chcę finału, w połączeniu filtr będzie seria musi-być szanowanym warunki, można powiedzieć, że każda z predykatów musi powrócić prawdziwą dla końcowy predykat, aby zwrócić true.

Aby to działało, predykaty muszą być połączone z AND. Tak, zapytanie SQL wynikające musi wyglądać następująco:

predicate1 AND predicate2 AND predicate3 ...

lepszy sposób połączyć te predykaty z AND jest łańcuchem Where operatorzy zapytanie do ostatecznego zapytania, na przykład:

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

Wynikowe zapytanie SQL połączy każdy z tych predykatów z AND. Właśnie to chciałem zrobić.

Jest łatwiejsze niż zhackowanie drzewa wyrażeń samodzielnie.

2

ja nie rozumiem, dlaczego to zrobić:

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

Kiedy można po prostu uniknąć Invoke całkowicie, tak:

innerPredicate = innerPredicate.Or(inPeriod); 

To powinno działać perfekcyjnie.


BTW, mam wrażenie, że jest to błąd z LINQKit tutaj (chyba, że ​​jest jakaś dokumentacja, która sugeruje, że nie obsługuje tego scenariusza).

Kiedy próbowałem ten kod podobny:

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 generowane następujące wyrażenie: kompozytowy

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

... który rzeczywiście ma niezwiązanego parametr (d NodeType = Parametr, Nazwa = "d").

uniku Invoke z first.Or(second).Expand() generuje doskonale sensowne:

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

'innerPredicate.Or (inPeriod.Invoke)' nie będzie się kompilować. Myślę, że miałeś na myśli 'innerPredicate.Or (inPeriod)'. – svick

+0

@svick: Oh, literówka! Powiedziałem, że należy unikać "Invoke", ale nie usunąłem go z makaronu. Dzięki. – Ani

+0

Jeśli nie wywołuję jawnie wyrażenia (jak zasugerowałeś: 'innerPredicate.Or (inPeriod))' Otrzymuję: __ Parametr 'f' nie był powiązany w określonym wyrażeniu zapytania LINQ to Entities .__ –

0

używam tych metod rozszerzeń:

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); 
    } 

}