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

Обратите внимание, что нет отдельной цитаты для поля "Содержание", которое является текстовым полем в базе данных.

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

Спасибо заранее.

Ответ 1

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

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

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

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

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

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

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

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, чтобы сделать это.

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

var result = data.Where(filter);

Передача сложного предиката, который мы только что создали.

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

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

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

Надеюсь, это поможет!


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

Ответ 2

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