2

次の列と対応するエンティティを持つ単一のテーブル (Car と呼ばれる) がある場合を考えてみましょう。

string Make
string Model
string Owner

ここで、検索の対象となるプロパティを (チェックボックスを使用して) ユーザーが選択できる検索を作成したいと考えています。複数を選択した場合、検索文字列がそれらの少なくとも 1 つに含まれていれば十分です。

さらに、(スペースで区切られた) 複数の検索文字列が指定された場合、検索はすべての単語が見つかった場合にのみ一致する必要があります (たとえば、検索文字列が「ter mist」の場合、所有者が「mister」の車が一致します)。

Expression<Func<Car, bool>>いくつかの調査を行った後、選択したプロパティごとに のリストを作成し、検索文字列の単語ごとに 1 つ追加してから、これらすべてをまとめて 1 つのExpression<Func<Car, bool>>. 選択したすべてのプロパティに対してこれらを取得したら、それらをまとめて、最終的なフィルターを作成します。しかし、これは私が苦労しているところです。

結局、私が得た最も遠いものは、NotSupportedException ということでしたThe LINQ expression node type 'Invoke' is not supported in LINQ to Entities.

結合を行うためのヘルパー関数を次に示します ( http://social.msdn.microsoft.com/Forums/en-US/linqprojectgeneral/thread/60a1f4c0-d4d9-4143-91aa-79d29dde7a7c/から見つかりました):

public static Expression<Func<T, bool>> Or<T>(params Expression<Func<T, bool>>[] predicates)
    {
        if (predicates.Length == 1)
            return predicates[0];

        Expression<Func<T, bool>> result = predicates[0];
        for (int i = 1; i < predicates.Length; i++)
        {
            result = OrTwo(result, predicates[i]);
        }

        return result;
    }
private static Expression<Func<T, bool>> OrTwo<T>(Expression<Func<T, Boolean>> expr1, Expression<Func<T, bool>> expr2)
    {
        var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
        return (Expression.Lambda<Func<T, Boolean>>(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters));
    }

これも驚くほどややこしくなってきているので、もっと簡単な方法があるに違いないと思い始めています。では、これを解決する最も簡単な方法は何でしょうか?


解決

いくつかのことを試した後 (LINQKit、Albahari の PredicateBuilder、自分で式ツリーをいじる)、最終的にここにたどり着きました。PredicateBuilder のこのユニバーサル バージョンは、他の外部依存関係なしで動作し、EF と完全に互換性があります。それは問題を解決することを本当に簡単にしました。

4

1 に答える 1

1

私は数週間前にこの問題を自分で解決することにしました。

私のブログ投稿をチェックしてください:

http://jnye.co/Posts/5/generic-repository-search-function-with-expression-trees

リンクの更新:検索機能を拡張メソッドとして IQueryable に追加する新しい投稿を作成しました。

http://jnye.co/Posts/6/c%23-generic-search-extension-method-for-iqueryable

ニーズに合わせて調整できるはずです。

更新しました

以下は、私が作成した検索機能の抜粋です。

/// <summary>  
/// Performs a search on the supplied string property  
/// </summary>  
/// <param name="stringProperty">Property to search upon</param>  
/// <param name="searchTerm">Search term</param>  
public virtual IQueryable<T> Search(Expression<Func<T, string>> stringProperty, string searchTerm)  
{  
    var source = this.RetrieveAll();  

    if (String.IsNullOrEmpty(searchTerm))  
    {  
        return source;  
    }  

    //Create expression to represent T.[property] != null  
    var isNotNullExpression = Expression.NotEqual(stringProperty.Body, Expression.Constant(null));  

    //Create expression to represent T.[property].Contains(searchTerm)  
    var searchTermExpression = Expression.Constant(searchTerm);  
    var checkContainsExpression = Expression.Call(stringProperty.Body, typeof(string).GetMethod("Contains"), searchTermExpression);  

    //Join not null and contains expressions  
    var notNullAndContainsExpression = Expression.AndAlso(isNotNullExpression, checkContainsExpression);  

    //Build final expression  
    var methodCallExpression = Expression.Call(typeof (Queryable),   
                                               "Where",   
                                               new Type[] {source.ElementType},   
                                               source.Expression,   
                                               Expression.Lambda<Func<Club, bool>>(notNullAndContainsExpression, stringProperty.Parameters));  

    return source.Provider.CreateQuery<T>(methodCallExpression);  
}  

methodCallExpressionを生成するコードをリファクタリングして、を使用して結合できる複数の検索式を作成できるはずですExpression.OrElse()

于 2013-03-19T13:47:23.977 に答える