5 Stimmen

LINQ-to-SQL-Prädikate zu einem einzigen Prädikat zusammensetzen

(Eine frühere Frage, LINQ-Prädikate rekursiv (?) zu einem einzigen Prädikat zusammensetzen ist ähnlich wie diese, aber ich habe eigentlich die falsche Frage gestellt... die Lösung dort erfüllte die gestellte Frage, ist aber eigentlich nicht das, was ich brauche. Sie sind aber unterschiedlich. Ehrlich.)

Geben Sie den folgenden Suchtext ein:

"keyword1 keyword2 ... keywordN"

Ich möchte die folgende SQL-Datei erhalten:

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%'
    )

Wir teilen den Suchtext an Leerzeichen auf, schneiden jedes Token ab, konstruieren eine mehrteilige ODER-Klausel auf der Grundlage jedes Tokens und verknüpfen dann die Klauseln mit UND.

Ich tue dies in Linq-to-SQL, und ich habe keine Ahnung, wie man dynamisch ein Prädikat auf der Grundlage einer beliebig langen Liste von Unterprädikaten komponieren. Für eine bekannte Anzahl von Klauseln, ist es einfach, die Prädikate manuell zu komponieren:

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

Kurz gesagt, ich brauche eine Technik, die bei zwei Prädikaten ein einzelnes Prädikat zurückgibt, das die beiden Quellprädikate mit einem bereitgestellten Operator zusammensetzt, aber auf die von Linq-to-SQL ausdrücklich unterstützten Operatoren beschränkt ist. Hat jemand eine Idee?

6voto

Thomas Levesque Punkte 277723

Sie können die PredicateBuilder Klasse

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

(das ist eigentlich das Beispiel aus der PredicateBuilder Seite, ich habe sie nur an Ihren Fall angepasst...)


EDIT :

Eigentlich habe ich Ihre Frage falsch verstanden, und mein obiges Beispiel deckt nur einen Teil der Lösung ab... Die folgende Methode sollte tun, was Sie wollen:

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

Sie können es so verwenden:

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

foreach (var Customer in query)
{
    ...
}

0voto

Marcelo Cantos Punkte 173498

Normalerweise würden Sie die Aufrufe von .Where(...) . Z.B.:

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 würde das alles wieder zu einem einzigen WHERE Klausel.

Dies funktioniert nicht mit OR Allerdings. Sie könnten Unions und Überschneidungen verwenden, aber ich bin nicht sicher, ob LINQ-to-SQL (oder SQL Server) clever genug ist, um es auf einen einzigen WHERE Klausel. Es macht aber nichts, wenn die Leistung nicht leidet. Jedenfalls würde es ungefähr so aussehen:

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

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X