7

私はこの問題の解決策を積極的に探していますが、スタック上の誰かがすでにこれを理解している場合、研究開発時間を短縮できると考えました。(オンラインで何も見つからなかったので、ここに行きます)

List<Expression<Func<T,bool>>>構築中のアプリケーション フレームワークには、一連の述語 ( ) を取り込み、それを検索フレームワークで解析する機能が必要なケースがあります。

現在、次のようにフィルタリングする機能があります。

//Assume predicates is passed as a method argument.
//     of List<Expression<Func<T,bool>>>
//Assume user is passed in as a method argument.
//Assume FilterToUserAccess is a custom extension method that restricts the dataset
//    to access restrictions.
var query = _dbContext.Set<EntityType>()
     .FilterToUserAccess(user);
foreach(var p in predicates){
     query = query.Where(p);
}

return p.ToList();

これを行う必要がある理由は、フィルタリング可能なオブジェクトのスケーラビリティのためです。ただし、EF の組み込み機能を考えると、クイック検索ではこれは不可能です。私ができる必要があるのは:

オブジェクト A (レーシング カーのように仮定します) をクイック検索ボックスで検索し、メーカー、モデル、チーム、およびドライバーを検索します。したがって、「Earnhardt」と入力すると、メーカー、モデル、チーム、ドライバーのすべてのレース カー エンティティ プロパティが検索されます。すべての DEI 車と Dale Jr. を使用することになります。同じアプローチを使用して、検索可能なエンティティを構成し、アプリケーションの起動時に検索構成を反映できるようにしたいと考えています。理想的には、クエリを次のように表示する方法をいくつか作成したいと思います。

//Assume predicates is passed as a method argument.
//     of List<Expression<Func<T,bool>>>
//Assume user is passed in as a method argument.
//Assume FilterToUserAccess is a custom extension method that restricts the dataset
//    to access restrictions.
var query = _dbContext.Set<EntityType>()
    .FilterToUserAccess(user);
foreach(var p in predicates){
    query = query.Or(p);
}

return p.ToList();

私はできることを理解しています:

_dbContext.Set<EntityType>().Where(predicate1 || predicate2 || predicate3)

ただし、これは、この問題を解決するために取りたいアプローチでは機能しません。理想的には、クライアント サイトの 1 つの管理者がアクセスして、シングル クリックで追加の検索用語を構成して、そのエンティティ タイプのすべてのクイック検索に含めることができます。.Where(...)"and" チェーン ロジック。

4

2 に答える 2

6

最初の解決策はうまくいきませんでした。

ステップ 1: LinqKit の NuGet パッケージをインストールします。

ステップ 2:以下のコードをお楽しみください

using (ISampleRepository repo = new SampleRepository())
{
    var predicates = new List<Expression<Func<Customer,bool>>>(){
        (x => x.FirstName.Contains(searchValue)),
        (x => x.LastName.Contains(searchValue))
    };

    var lambda = PredicateBuilder.False<Customer>();
    lambda = predicates.Aggregate(lambda, (current, p) => current.Or(p).Expand());

    var query = repo.QueryCustomers().AsExpandable().Include(x => x.Phones).Where(lambda);
    return query.Take(500)
        .ToList()
        .Select(x => x.ToDTO())
        .ToList();
}

これは単なるスパイクサンプルですが、メソッド取り込みで同じことを行っています ->

List<T> QuickSearch<T>(string input) ...

同じアプローチを使用できるようになります。述語のコレクションがまだ Expression 形式で渡されているので、述語ビルダーのトリックを使用してクエリを実行します。次に、AsExpandable() を使用すると、述語ビルダーを使用して作成された結合された述語を実行できます。

これが私以外にも役立つことを願っていますが、これはコードがかなり少ないため、私が使用するソリューションです。他の場所で述語を作成できます...そして、事後に「OR」ステートメントでそれらを結合します。

于 2012-08-27T19:11:32.223 に答える
2

Ladislavが言うように、LINQ式を動的に生成する必要があります。整数のコレクションの述語を動的に構築するプログラムの簡単な例を次に示します。

class Program {
    static void Main(string[] args) {

        // Retreive your data source
        List<int> numbers = new List<int>() { 0, 10, 20, 30, 40, 50, 60 };

        // Create a collection of predicates that you would like to chain together.
        ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "x");
        List<Expression> predicates = new List<Expression>();

        // x >= 50
        predicates.Add(Expression.GreaterThanOrEqual(parameterExpression, Expression.Constant(50)));

        // x <= 20
        predicates.Add(Expression.LessThanOrEqual(parameterExpression, Expression.Constant(20)));

        // Build a single predicate by chaining individual predicates together in an OR fashion
        Expression whereFilter = Expression.Constant(false); // Use false a base expression in OR statements

        foreach (var predicate in predicates) {
            whereFilter = Expression.OrElse(whereFilter, predicate);
        }

        // Once the expressions have been chained, create a lambda to represent the whole predicate
        // x => (x >= 50) || (x <= 20)
        Expression<Func<int, bool>> whereLambda = 
            (Expression<Func<int, bool>>)Expression.Lambda(whereFilter, 
                                            new List<ParameterExpression>() { parameterExpression });

        // To use an expression directly, the datasource must be an IQueryable
        // Since I am using List<T> I must call AsQueryable.  This is not necessary
        // if your collection is already IQueryable, like in Entity Framework.
        var results = numbers.AsQueryable().Where(whereLambda);

    }
}

基本的に、ここで行うのは、いくつかのブールステートメント(x> = 50)および(x <= 20)を作成し、それらをコレクションに配置することだけです。次に、そのコレクションをループして、各ステートメントを取得し、最後のステートメントにORします。結果は、すべてORによってリンクされた一連のブールステートメントです。次に、そのステートメントをLambda式でラップしIQueryable.Whereて、クエリ可能なコレクションで使用して渡すことができるようにします。結果は、私のフルセットからのフィルタリングされた整数のセットです。

LINQ式は控えめに言っても混乱する可能性がありますが、非常に強力で、知っておく価値があります。この例をより理解するために私にできることがあれば教えてください。

于 2012-08-27T19:12:03.997 に答える