5

(以前の質問、Recursively (?) compose LINQ predicates into a single predicateはこれに似ていますが、実際には間違った質問をしました...そこでの解決策は提起された質問を満たしましたが、実際には私が必要としているものではありません。違いますが、正直です。)

次の検索テキストがあるとします。

"keyword1 keyword2 ... keywordN"

私は次のSQLで終わりたい:

SELECT [columns] FROM Customer 
  WHERE (
        Customer.Forenames LIKE '%keyword1%' 
        OR 
        Customer.Forenames LIKE '%keyword2%'
        OR
        ...
        OR
        Customer.Forenames LIKE '%keywordN%'
    ) AND (
        Customer.Surname LIKE '%keyword1%' 
        OR 
        Customer.Surname LIKE '%keyword2%'
        OR
        ....
        OR
        Customer.Surname LIKE '%keywordN%'
    )

事実上、検索テキストをスペースで分割し、各トークンをトリミングし、それぞれに基づいてマルチパート OR 句を作成し、句を AND 結合します。

私はこれをLinq-to-SQLで行っていますが、サブ述語の任意の長いリストに基づいて述語を動的に作成する方法がわかりません。句の数がわかっている場合、述語を手動で構成するのは簡単です。

dataContext.Customers.Where(
    ( 
      Customer.Forenames.Contains("keyword1") 
      ||
      Customer.Forenames.Contains("keyword2")
    ) && (
      Customer.Surname.Contains("keyword1") 
      ||
      Customer.Surname.Contains("keyword2")
    )
);

要するに、2 つの述語が与えられた場合に、指定された演算子を使用して 2 つのソース述語を構成する単一の述語を返す手法が必要ですが、Linq-to-SQL で明示的にサポートされている演算子に限定されます。何か案は?

4

2 に答える 2

6

PredicateBuilderクラスを使用できます

IQueryable<Customer> SearchCustomers (params string[] keywords)
{
  var predicate = PredicateBuilder.False<Customer>();

  foreach (string keyword in keywords)
  {
    // Note that you *must* declare a variable inside the loop
    // otherwise all your lambdas end up referencing whatever
    // the value of "keyword" is when they're finally executed.
    string temp = keyword;
    predicate = predicate.Or (p => p.Forenames.Contains (temp));
  }
  return dataContext.Customers.Where (predicate);
}

(これは実際にはPredicateBuilderページの例です。あなたのケースに合わせただけです...)


編集:

実際、私はあなたの質問を読み違えており、上記の例は解決策の一部しかカバーしていません...次の方法はあなたが望むことをするはずです:

IQueryable<Customer> SearchCustomers (string[] forenameKeyWords, string[] surnameKeywords)
{
    var predicate = PredicateBuilder.True<Customer>();

    var forenamePredicate = PredicateBuilder.False<Customer>();
    foreach (string keyword in forenameKeyWords)
    {
      string temp = keyword;
      forenamePredicate = forenamePredicate.Or (p => p.Forenames.Contains (temp));
    }
    predicate = PredicateBuilder.And(forenamePredicate);

    var surnamePredicate = PredicateBuilder.False<Customer>();
    foreach (string keyword in surnameKeyWords)
    {
      string temp = keyword;
      surnamePredicate = surnamePredicate.Or (p => p.Surnames.Contains (temp));
    }
    predicate = PredicateBuilder.And(surnamePredicate);

    return dataContext.Customers.Where(predicate);
}

次のように使用できます。

var query = SearchCustomers(
    new[] { "keyword1", "keyword2" },
    new[] { "keyword3", "keyword4" });

foreach (var Customer in query)
{
    ...
}
于 2010-09-23T22:34:38.153 に答える
0

通常、の呼び出しをチェーンします.Where(...)。例えば:

var a = dataContext.Customers;
if (kwd1 != null)
    a = a.Where(t => t.Customer.Forenames.Contains(kwd1));
if (kwd2 != null)
    a = a.Where(t => t.Customer.Forenames.Contains(kwd2));
// ...
return a;

LINQ-to-SQLは、すべてを1つのWHERE句に統合します。

ただし、これはでは機能しませんORWHERE和集合と共通部分を使用することもできますが、LINQ-to-SQL(またはSQL Server)が単一の句に折り返すのに十分賢いかどうかはわかりません。OTOH、パフォーマンスが低下しなくても構いません。とにかく、それは次のようになります:

<The type of dataContext.Customers> ff = null, ss = null;

foreach (k in keywords) {
    if (keywords != null) {
        var f = dataContext.Customers.Where(t => t.Customer.Forenames.Contains(k));
        ff = ff == null ? f : ff.Union(f);

        var s = dataContext.Customers.Where(t => t.Customer.Surname.Contains(k));
        ss = ss == null ? s : ss.Union(s);
    }
}
return ff.Intersect(ss);
于 2010-09-23T22:25:55.593 に答える