2009-02-28 5 views
11

У меня есть таблица курсов, которую мне нужно искать на основе ключевых слов, введенных в поле поиска. Вот пример запроса:LINQ multiple where clause

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

Как я могу преобразовать это в LINQ, где LINQ будет динамически генерировать WHERE заявления, основанные на каждом ключевых слов.

Я пытался использовать PredicateBuilder, он отлично работает, если поле VARCHAR. Для полей «ТЕКСТ» кавычки не генерируются, поэтому компилятор должен дать сообщение об ошибке. Вот SQL генерируется 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%) 

Уведомление нет ни одного Quote для поля «Содержание», который является текстовым полем, в базе данных.

Есть ли простой способ построить инструкцию WHERE и прикрепить ее к запросу? Кто-нибудь знает, как я могу это сделать без PredicateBuilder?

Заранее спасибо.

ответ

13

Поскольку вы работаете с LINQ, я полагаю, вы работаете с контекстом данных LINQ-to-SQL? У меня нет запасного DataContext, чтобы проверить это, но это должно дать вам некоторые идеи.

Я не знаю, будет ли он работать с контекстом данных, но большинство из них довольно простые (привязка оператора OR и вызов метода Contains), поэтому он не должен вызывать проблемы, когда запрос переводится в SQL.

Сначала я создать пользовательскую функцию, которая будет строить свой предикат:

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

Это функция, которая принимает один строковый ключевое слово, а затем возвращать другую функцию, которая принимает DataItem и проверяет его против ключевого слова.

В принципе, если вы перейдете в «Stack», вы получите предикат: x => x.Title.Contains("Stack") || x.Contents.Contains("Stack").

Далее, поскольку существует много возможных ключевых слов, и вы должны приковать его с операцией ИЛИ, я создаю еще одну вспомогательную функцию в цепи 2 предикаты вместе с ИЛИ

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

Эта функция принимает 2 предикаты, а затем присоедините их к операции ИЛИ.

Имея эти 2 функции, я могу затем построить мой, где предикат, как это:

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

Первая строка внутри цикла в основном проверяет, если фильтр является недействительным. Если да, то мы хотим, чтобы для нас был создан простой фильтр ключевых слов.

Иначе, если фильтр не является нулевым, нам необходимо связать существующие фильтры с операцией ИЛИ, поэтому мы передаем существующий фильтр и новый фильтр ключевых слов для созданияOrPredicate, чтобы сделать именно это.

И тогда мы можем теперь создать где часть запроса:

var result = data.Where(filter); 

Passing в сложном предикате мы только что создали.

Я не знаю, будет ли это отличаться от использования PredicateBuilder, но поскольку мы откладываем перевод запросов к движку LINQ-to-SQL, проблем не должно быть.

Но, как я уже сказал, я не тестировал его в реальном контексте данных, поэтому, если есть какие-либо проблемы, вы можете написать в комментариях.

Вот консоль приложение, которое я построил для теста: http://pastebin.com/feb8cc1e

Надеется, что это помогает!


EDIT: Для более общего и многократного использования версии, которая включает в себя должным образом с использованием деревьев выражений в LINQ, проверить блоге Томаса Petricek в: http://tomasp.net/articles/dynamic-linq-queries.aspx

+1

Это будет работать только к сожалению для функций. Чтобы сделать эту работу с деревьями выражений, вам нужно использовать трюк, подобный этому: http://tomasp.net/articles/dynamic-linq-queries.aspx –

+0

Это какой-то подвиг, который вы там сделали! .. Тот же трюк, но более общий и еще больше удивительности ... Во всяком случае, я подписчик вашего блога сейчас :-) – chakrit

+0

Спасибо, этот работал. http://tomasp.net/articles/dynamic-linq-queries.aspx - Tomas Petricek – Amir

0

Поскольку предиктор-строитель не знает тип DB-типа, вызываемого методом Содержит, я предполагаю, что это может быть проблемой внутри linq-sql. Вы пробовали с обычным запросом (а не с построителем предикатов) и столбцом TEXT с Contains?