2009-02-28 15 views
11

Ho una tabella dei corsi che ho bisogno di cercare in base alle parole chiave digitate nella casella di ricerca. Ecco una query di esempio:LINQ multiple where clausola

SELECT * FROM Courses WHERE 
Title LIKE '%word%' OR Title LIKE '%excel%' OR 
Contents LIKE '%word%' OR Contents LIKE '%excel%' 

Come posso convertire questo in LINQ in cui LINQ sarebbe generare dinamicamente Se le dichiarazioni basate su ciascuna parole chiave.

Ho provato ad usare PredicateBuilder funziona bene finché il campo è VARCHAR. Per i campi "TESTO" le virgolette non vengono generate, facendo in modo che il compilatore fornisca un messaggio di errore. Ecco l'SQL generato da PredicateBuilder

SELECT [t0].[CoursesID], [t0].[Title], [t0].[Contents], [t0].[Active], 
FROM [dbo].[Courses] AS [t0] 
WHERE ([t0].[Title] LIKE '%word%') OR ([t0].[Contents] LIKE %word%) OR 
([t0].Title] LIKE '%excel%') OR ([t0].[Contents] LIKE %excel%) 

Avviso Non c'è una sola citazione per il campo "Contenuto", che è un campo di testo nel database.

Esiste un modo semplice per creare l'istruzione WHERE e allegarla alla query? Qualcuno sa come posso fare questo senza PredicateBuilder?

Grazie in anticipo.

risposta

13

Dato che si sta lavorando con LINQ, suppongo si stia lavorando contro un contesto dati LINQ-to-SQL, giusto? Non ho un DataContext di riserva in giro per testarlo, ma questo dovrebbe darti qualche idea.

Non so se funzionerà comunque con il contesto dei dati, ma la maggior parte di questi sono cose piuttosto semplici (concatenamento di operatore OR e chiamata di metodi Contains) quindi non dovrebbe causare problemi quando la query viene tradotta in SQL.

prima cosa creare una funzione personalizzata che avrebbe costruito la mia predicato:

Func<string, Func<DataItem, bool>> buildKeywordPredicate = 
    keyword => 
     x => x.Title.Contains(keyword) 
      || x.Contents.Contains(keyword); 

Si tratta di una funzione che prende una singola parola corda e poi tornare un'altra funzione che prende un DataItem e controlla contro la parola chiave.

Fondamentalmente, se si passa in "Stack", si ottiene un predicato: x => x.Title.Contains("Stack") || x.Contents.Contains("Stack").

successivo, dato che ci sono molte parole chiave possibili e avete bisogno di catena con un'operazione OR, creo un'altra funzione di supporto per catena 2 predicati insieme con un OR

Func<Func<DataItem,bool>, Func<DataItem, bool>, Func<DataItem, bool>> buildOrPredicate = 
    (pred1, pred2) => 
     x => pred1(x) || pred2(x); 

Questa funzione prende 2 predicati e poi unisciti a loro con un'operazione OR.

Avendo questi 2 funzioni, posso quindi costruire il mio in cui predicato in questo modo:

foreach (var word in keywords) {    
    filter = filter == null 
     ? buildKeywordPredicate(word) 
     : buildOrPredicate(filter, buildKeywordPredicate(word)); 
} 

La prima riga all'interno del ciclo controlla in fondo se il filtro è nullo. Se lo è, allora vogliamo un semplice filtro per le parole chiave costruito per noi.

Altrimenti se il filtro non è nullo, è necessario concatenare i filtri esistenti con un'operazione OR, quindi passiamo il filtro esistente e un nuovo filtro di parole chiave a buildOrPredicate per fare proprio questo.

E poi possiamo ora creare la parte in cui della query:

var result = data.Where(filter); 

Passando nel predicato complicato che abbiamo appena costruito.

Non so se questo sarà diverso dall'utilizzo di PredicateBuilder, ma dal momento che stiamo rimandando la traduzione di query al motore LINQ-to-SQL, non dovrebbero esserci problemi.

Ma come ho detto, non l'ho provato rispetto a un contesto di dati reali, quindi se ci sono problemi puoi scrivere nei commenti.

Ecco il console app che ho costruito a prova: http://pastebin.com/feb8cc1e

Spero che questo aiuti!


EDIT: Per una versione più generica e riutilizzabile che coinvolge correttamente utilizzando le Expression Trees in LINQ, check-out post sul blog di Thomas Petricek: http://tomasp.net/articles/dynamic-linq-queries.aspx

+1

Ciò sfortunatamente funziona solo per le funzioni. Per farlo funzionare con Expression Trees devi usare un trucco del genere: http://tomasp.net/articles/dynamic-linq-queries.aspx –

+0

Questa è una prodezza che hai fatto lì! .. Lo stesso trucco ma più generico e più meraviglia ... Ad ogni modo, ora sono iscritto al tuo blog :-) – chakrit

+0

Grazie, questo ha funzionato. http://tomasp.net/articles/dynamic-linq-queries.aspx - Tomas Petricek – Amir

0

Poiché il generatore di predicati non conosce il tipo di DB della proprietà in cui viene chiamato il metodo Contains, penso che questo potrebbe essere un problema all'interno di linq in sql. Hai provato con una query normale (non con il generatore di predicati) e una colonna TEXT con Contains?