5

を使用してフィルタとして機能するようにカスタム述語の構築:は私が<code>foreach</code>ループを使用して動的に<strong>ビルド</strong>に苦労していたカスタムフィルタに渡すことにより、ドキュメントのリストをフィルタリングする必要があるforeachループ

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

この最後の行:

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

はこの例外をスローします:

パラメータ 'd'は、指定されたLINQ to Entities クエリ式でバインドされていませんでした。

なぜこの例外が発生するのですか? InPeriodメソッドに渡す 'd'パラメータがどこに失われるのか分かりません。私はこれが機能するために何が欠けているのか分からない。私のコードは完璧に動作する他の多くの例と同じです。表現を呼び出すことについての理論的な情報や、その背後でどのように動作するかについての理論的な情報は歓迎されます。

+0

'mainPredicate.Expand()。Compile()。Invoke(someDocument)'を実行するとどうなりますか?あなたは問題が他の場所ではないと確信していますか? – svick

+0

あなたの提案があれば、最後の行を次のように変更しました: 'documentEntries = this.ObjectSet.AsExpandable()。where(de => mainPredicate.Expand()。Compile()。Invoke(de))。ToList() ); 'しかし、それは:LINQ to Entitiesはメソッド 'Boolean Invoke(Remabec.PM.Tarification.DocumentEntry)'メソッドを認識せず、このメソッドをストア式に変換することはできません。 –

+0

ええ、それは動作しません、それは私が意味するものではありません。自分でコードを実行してみることができますか? – svick

答えて

1

最後に、私はメインの式に複数の述語を組み合わせ回避する方法を発見しました木。

各述語が異なるフィルタを表し、私は最終的に、組み合わせフィルタは-でなければならない尊敬一連の条件になりたいことを考えると、我々は、述語のそれぞれがためを返すために持っていると言うことができますtrueを返す最終述語。

これが機能するには、述語を と組み合わせる必要があります。だから、結果のSQLクエリは次のようになります。

predicate1 AND predicate2 AND predicate3 ...

ANDでこれらの述語を結合するためのより良い方法は、チェーンには、このような最後のクエリにWhereクエリ演算子、次のとおりです。

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

結果のSQLクエリは、これらの述語のそれぞれをANDと組み合わせます。それは私がやりたいことです。

自分で式ツリーをハッキングするよりも簡単です。

2

あなたがこれを行う理由を私は理解していない:

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

あなたはこのように、完全にInvokeを避けることができた場合:

innerPredicate = innerPredicate.Or(inPeriod); 

これは完全に正常に動作する必要があります。


ところで、私は(それがこのシナリオをサポートしていないことを示唆しているいくつかのドキュメントがありますしない限り)バグはここにLINQKitであります感じています。

私はこの類似コードをしようとしたときに:

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は、以下の合成式発生:

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

...実際に結合していないパラメータDを(持ちますNodeType =パラメータ、Name = 'd')。 first.Or(second).Expand()Invokeを避け

は完全に賢明なを生成します。

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

'innerPredicate.Or(inPeriod.Invoke)'はコンパイルされません。私はあなたが 'innerPredicate.Or(inPeriod)'を意味したと思います。 – svick

+0

@svick:ああ、タイプミス!私は「Invoke」を避け、コピーパスタから自分自身を削除しないと言った。ありがとう。 – Ani

+0

明示的に式を呼び出さなかった場合( 'innerPredicate.Or(inPeriod))'と表示されました:__パラメータ 'f'が指定されたLINQ to Entitiesクエリ式にバインドされていません.__ –

0

は、私はこれらの拡張メソッドを使用します。

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

} 
関連する問題

 関連する問題