Как повторно использовать where clauses в запросах Linq To Sql
У меня есть пользователи, которые ищут записи типа Record. Они вводят термин поиска в текстовое поле, а затем я ищу записи, сопоставляя несколько полей с поисковым термином.
Мой запрос выглядит так:
var results = from record in DataContext.Records
where
record.Field1.ToLower().Contains(term) ||
record.Field2.ToLower().Contains(term) ||
record.Field3.ToLower().Contains(term)
select record;
У меня есть ряд запросов, которые используют один и тот же фильтр, и поэтому я хотел бы извлечь фильтрацию, чтобы ее можно было повторно использовать. Что-то вроде:
var filter = new Func<Record, string, bool>(
(record, term) =>
record.Field1.ToLower().Contains(term) ||
record.Field2.ToLower().Contains(term) ||
record.Field3.ToLower().Contains(term)
);
var results = from record in DataContext.Records
where filter(record, term)
select record;
Однако это не работает, потому что:
Метод 'System.Object DynamicInvoke (System.Object [])' не поддерживает перевод на SQL.
Как я могу повторно использовать мое условие в запросах?
Ответы
Ответ 1
Используйте CompiledQuery!
var filter = CompiledQuery.Compile(
(DatabaseDataContext dc, Record record, string term) =>
record.Field1.ToLower().Contains(term) ||
record.Field2.ToLower().Contains(term) ||
record.Field3.ToLower().Contains(term)
);
var results = from record in DataContext.Records
where filter(DataContext, record, term)
select record;
Для получения дополнительной информации см. Практическое руководство. Сохранение и повторное использование запросов.
Ответ 2
Вам нужно создать выражение вместо функции:
Expression<Func<Record, bool>> filter =
record => record.Field1.ToLower().Contains(term); // rest omitted
Выражение лямбда остается неизменным, но вам нужно вернуть его в переменную типа Expression<Func<Record, bool>>
-, которая заставит компилятор С# скомпилировать его как выражение вместо делегата, что позволит ему передать LINQ в SQL.
Однако вы не сможете использовать переменную выражения с предложением С# -syntax where: вам нужно использовать метод расширения Where:
var results = DataContext.Records.Where(filter);
Отредактировано для добавления: Если вы хотите иметь возможность создавать фильтры на разных терминах, вам просто нужен метод для создания выражения из термина:
private static Expression<Func<Record, bool>> Filter(string term)
{
return r => r.Field1.ToLower().Contains(term);
}
var results = DataContext.Records.Where(Filter(term));
Если вы предпочитаете держать filter
как лямбда, как у вас на данный момент, вы можете сделать это, но генерики немного вложены:
Func<string, Expression<Func<Record, bool>>> filter =
term => (r => r.Field1.ToLower().Contains(term));
var results = DataContext.Records.Where(filter(term));
Независимо от того, что важно в предложении Where должно быть Expression<Func<Record, bool>>
, но, как показано выше, вы можете заставить выражение зависеть от term
, построив подходящее выражение "на лету". Это именно то, что LINQ to SQL будет делать, если вы изложили фильтр в команде Where.
Ответ 3
В дополнение к проблеме Expression<Func<Record, bool>>
, которую другие указали, я предлагаю изучить PredicateBuilder. Это очень удобно для динамического объединения лямбда-выражений.
Ответ 4
Я думаю, вам нужно сделать это Expression<Func<Record, bool>>
. В противном случае он пытается перевести вызов метода С# на SQL, а не его описание. Это не гарантия того, что эта версия будет работать; Я не уверен, какие строковые функции могут быть переведены в SQL.