10

Entity Framework アプリケーションの単純な検索クエリを作成しています。一連のフィールドが null かどうかを確認する必要があります。そうでない場合は、ToLower() を呼び出して、検索クエリと比較します。LINQ クエリは次のようになります。

public IQueryable<Store> SearchStores(string q, IQueryable<Store> source)
{
    q = q.ToLower();

    return (
        from s in source
        where (
            (s.Name != null && s.Name.ToLower().Contains(q)) ||
            (s.Description != null && s.Description.ToLower().Contains(q)) ||
            ...
}

このような行がたくさんあるので、ヘルパー メソッドを書いて少し整理したくなりました。

public static bool SafeSearch(this string s, string q)
{
    return s == null ? false : s.ToLower().Contains(q);
}

ただし、エンティティへの LINQ は SafeSearch 関数が何であるかを理解していないため、これはもちろん機能しません。

LINQ to Entities はメソッド 'Boolean SafeSearch(System.String, System.String)' メソッドを認識せず、このメソッドはストア式に変換できません。

このような単純なカスタム関数を簡単に作成する方法はありますか?

ありがとう!

4

2 に答える 2

4

動的クエリと条件を準備する方法と、関数を使用してそれらの一部を構築する方法があります。構文も読みやすく、質問の「単純な」部分に適しています。Linq 式を組み合わせることで可能です。これを行う方法についてはいくつかの記事がありますが、新しいアプローチを思いついたと思います。少なくとも私はウェブ上で見つけられませんでした。

続行するには、3 つの単純な関数のライブラリが必要です。System.Linq.Expressions.ExpressionVisitor式を動的に変更するために使用します。重要な機能は、同じ名前を持つ 2 つのパラメーターが同一になるように、式内のパラメーターを統合することです ( UnifyParametersByName)。残りの部分は、名前付きパラメーターを指定された式 ( ReplacePar) とヘルパー メソッド ( NewExpr) に置き換えています。このライブラリは、github: LinqExprHelperで MIT ライセンスで利用できますが、自分で何かをすばやく作成することもできます。

最初にいくつかのメソッドを定義します。これは後で動的クエリの作成に使用できます。

public class Store
{
    ...

    public static Expression<Func<Store, bool>>
        SafeSearchName(string sWhat)
    {
        return LinqExprHelper.NewExpr(
            (Store s) => s.Name != null && s.Name.ToLower().Contains(sWhat)
        );
    }

    public static Expression<Func<Store, bool>>
        SafeSearchDesc(string sWhat)
    {
        return LinqExprHelper.NewExpr(
            (Store s) => s.Description != null && s.Description.ToLower().Contains(sWhat)
        );
    }
}

次に、次の方法でクエリを実行します。

    // Define a master condition, using named parameters.
    var masterExpr = LinqExprHelper.NewExpr(
        (Store s, bool bSearchName, bool bSearchDesc)
        => (bSearchName && bSearchDesc));

    // Replace stub parameters with some real conditions.
    var combExpr = masterExpr
        .ReplacePar("bSearchName", Store.SafeSearchName("b").Body)
        .ReplacePar("bSearchDesc", Store.SafeSearchDesc("p").Body);
        // Sometimes you may skip a condition using this syntax:
        //.ReplacePar("bSearchDesc", Expression.Constant(true));

    // It's interesting to see how the final expression looks like.
    Console.WriteLine("expr: " + combExpr);

   // Execute the query using combined expression.
   db.Stores
        .Where((Expression<Func<Store, bool>>)combExpr)
        .ToList().ForEach(i => { Console.WriteLine(i.Name + ", " + i.Description); });

これはまだ本番環境では使用していませんが、いくつかの簡単なテストには合格しています。このようにクエリを組み合わせることに制限はありません。より多くのパラメーターが必要な場合は、結合の追加レベルを追加できます。この方法の利点は、読みやすいインライン ラムダ式と、非常に優れた動的な式の作成および合成を使用できることです。

やっぱり「シンプル」?Linq のメソッド構文が単純であると考えれば、これはほぼ単純です。カスタム Linq 関数を作成することはできませんが、同等の機能が提供されます。

于 2016-10-16T18:58:51.383 に答える
4

linq は実際にデータベースを呼び出すまで実行されない式を使用するため、関数を述語内にラップする必要があります。

private static Func<Country, bool> Predicate(string q)
{
    return x => (
        q.SafeSearch(x.Name) ||
        q.SafeSearch(x.Description)
        );
}

また、クエリで呼び出して SafeSearch 拡張メソッドを逆にすると、x.Name が null の場合に対処できます。

public static class SearchExt
{
    public static bool SafeSearch(this string q, string param)
    {
        return param == null ? false : param.ToLower().Contains(q);
    }
}

そして、拡張メソッドでそれを使用できます

return source.Where(Predicate(q));

またはlinq式を使用して

return from p in source
       where Predicate(q).Invoke(p)
       select p;
于 2012-05-07T16:26:40.777 に答える