Рекурсивно (?) Объединять предикаты LINQ в один предикат - PullRequest
2 голосов
/ 24 сентября 2010

(РЕДАКТИРОВАТЬ: Я задал неправильный вопрос. У меня проблема real закончилась на Объединить предикаты LINQ-to-SQL в один предикат - но этотполучил несколько хороших ответов, поэтому я оставил это!)

Учитывая следующий текст поиска:

"keyword1 keyword2 keyword3   ... keywordN"

Я хочу получить следующий SQL:

SELECT [columns] FROM Customer 
  WHERE 
    (Customer.Forenames LIKE '%keyword1%' OR Customer.Surname LIKE '%keyword1%')
  AND
     (Customer.Forenames LIKE '%keyword2%' OR Customer.Surname LIKE '%keyword2%')
  AND 
    (Customer.Forenames LIKE '%keyword3%' OR Customer.Surname LIKE '%keyword3%')
  AND
    ...
  AND 
    (Customer.Forenames LIKE '%keywordN%' OR Customer.Surname LIKE '%keywordN%')

По сути, мы разбиваем текст поиска на пробелы, обрезаем каждый токен, строим предложение OR из нескольких частей на основе каждого токена, а затем объединяем предложения AND.

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

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

, но я хочу обработать произвольный список поисковых терминов.Я дошел до

Func<Customer, bool> predicate = /* predicate */;
foreach(var token in tokens) {
    predicate = (customer 
        => predicate(customer) 
        && 
         (customer.Forenames.Contains(token) || customer.Surname.Contains(token));
}

, который создает исключение StackOverflowException - предположительно потому, что предикат () в RHS назначения фактически не оценивается до времени выполнения, после чего он в конечном итоге вызывает сам себя ...или что-то в этом роде.

Короче говоря, мне нужен метод, который при двух предикатах возвращает один предикат, составляющий два исходных предиката с предоставленным оператором, но ограниченный операторами, явно поддерживаемыми Linq-to-SQL.,Есть идеи?

Ответы [ 2 ]

3 голосов
/ 24 сентября 2010

Я бы предложил другую технику

, которую вы можете сделать:

var query = dataContext.Customers;

и затем, внутри цикла, выполните

foreach(string keyword in keywordlist)
{
    query = query.Where(Customer.Forenames.Contains(keyword) || Customer.Surname.Contains(keyword));
}
1 голос
/ 24 сентября 2010

Если вы хотите более сжатый и декларативный способ написания этого, вы также можете использовать Aggregate метод расширения вместо foreach цикла и изменяемой переменной:

var query = keywordlist.Aggregate(dataContext.Customers, (q, keyword) => 
    q.Where(Customer.Forenames.Contains(keyword) || 
            Customer.Surname.Contains(keyword)); 

Это принимает dataContext.Customers какначальное состояние, а затем обновляет это состояние (запрос) для каждого ключевого слова в списке, используя данную функцию агрегирования (которая просто вызывает Where, как предлагает Gnomo.

...