2010-09-23 14 views
5

(Wcześniejsze pytanie, Recursively (?) compose LINQ predicates into a single predicate, jest podobne do tego, ale faktycznie zadałem złe pytanie ... rozwiązanie tam spełniło postawione pytanie, ale tak naprawdę nie jest muszę są one różne, choć Honest)Skomponuj predykaty LINQ-SQL do jednego predykatu

Biorąc pod uwagę następujący tekst wyszukiwania:...

"keyword1 keyword2 ... keywordN" 

Chcę skończyć z następującym 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%' 
    ) 

W efekcie dzielimy szukany tekst na spacje, przycinamy każdy token, konstruujemy wieloczęściową klauzulę OR na podstawie każdego z nich, a następnie łączymy klauzule AND.

Robię to w Linq-SQL i nie mam pojęcia, jak dynamicznie komponować predykat oparty na arbitralnie długiej liście podprogowych. Dla znanej liczby klauzul łatwo ręcznie komponować orzeczniki:

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

Krótko mówiąc, potrzebna jest techniką, która, podawana dwa orzeczniki powróci jeden orzeczenie tworzenia dwa źródła predykaty z operatorem dostarczonych ale ograniczone do operatorów wyraźnie obsługiwanych przez Linq-SQL. Jakieś pomysły?

+0

To może być duplikat : http://stackoverflow.com/questions/180405/how-do-you-add-dynamic-where-clauses-to-a-linq-query – devuxer

+0

Twoje zapytanie nie ma sensu ... Chcesz znaleźć klientów, gdzie * Imię * zawiera co najmniej jedno z wyszukiwanych haseł, a * Nazwisko * zawiera co najmniej jedno z wyszukiwanych haseł itp.? Czy zapytanie nie powinno znaleźć klientów, którzy mają * wszystkie * wyszukiwane hasła, ale w polu * dowolne *? – Timwi

Odpowiedz

6

Można użyć klasy

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 (to faktycznie przykład ze strony PredicateBuilder, po prostu dostosować go do swojego przypadku ...)


EDIT:

Właściwie misread swoją questi na, i mój przykład powyżej obejmuje tylko część roztworu ... Poniższa metoda powinna zrobić to, co chcesz:

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

Można go używać tak:

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

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

Geniusz. Bardzo zobowiązany. –

0

Zazwyczaj wywoływane są łańcuchy wywołań .Where(...). Np .:

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-SQL byłoby to wszystko z powrotem zespawać razem w jednym WHERE klauzuli.

To jednak nie działa z OR. Możesz użyć związków i skrzyżowań, ale nie jestem pewien, czy LINQ-SQL (lub SQL Server) jest na tyle sprytny, aby złożyć go z powrotem do pojedynczej klauzuli WHERE. OTOH, nie ma znaczenia, jeśli wydajność nie ucierpi. W każdym razie, to będzie wyglądać mniej więcej tak:

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

Jak robisz LUB używasz tego? –

+0

Ah, głupi mnie. Nie zwracałem uwagi na '||' s. –

+0

Poprawiłem moją odpowiedź. 'PredicateBuilder' wygląda na lepsze rozwiązanie, ale na wszelki wypadek zostawiam moje. –

Powiązane problemy