#sql-server #.net-core #linq-to-sql #entity-framework-core
#sql-сервер #.net-ядро #linq-to-sql #entity-framework-core
Вопрос:
Использование Entity Framework Core 3.1.7
У меня есть таблица в базе данных, содержащая продукты.
public class Product
{
public int Id {get; set;}
public string Name {get; set;}
}
Затем я хочу, чтобы пользователь мог использовать поле поиска для поиска определенных продуктов в таблице пользовательского интерфейса.
Когда приходит запрос, я пытаюсь выполнить следующее:
var searchParameters = query.SearchParameters.ToLower().Split(' ', ',', ' ').Distinct();
var result = _context.Products
.Where(p => searchParameters.Any()
amp;amp; (searchParameters.Any(x => p.Name.ToLower().Contains(x)) //Version 1
).ToList();
или альтернатива
searchParameters.Any(x => EF.Functions.Like(p.Name, "%" x "%")) //Version 2
Но, однако, я настраиваю эту, казалось бы, простую вещь, которую я получаю:
Выражение LINQ ‘DbSet .Где(p => __searchParameters_0 .Any(x => p.Name .Не удалось перевести ToLower().Contains(x)))’. Либо перепишите запрос в форме, которая может быть переведена, либо переключитесь на оценку клиента явно, вставив вызов AsEnumerable(), AsAsyncEnumerable(), ToList() или ToListAsync()
Я понимаю, что .ToLower()
это будет проблемой, поэтому я хотел запустить оператор LIKE для поиска без учета регистра, как и для запросов SQL. Но даже в этом случае List<string>
не переводится.
Комментарии:
1. Обратите внимание, что в зависимости от настроек вашего SQL Server сопоставление без учета регистра вполне может быть по умолчанию, поэтому вам ничего не нужно делать, чтобы поиск был переведен незаметно.
2. @NetMage Да, спасибо! Я попытался пренебречь ToLower, и он может найти результаты, но только если я выполняю один запрос по параметрам поиска с момента . Any не переводится.
Ответ №1:
Если вы хотите использовать LinqKit (или имитировать части построения предикатов), вы можете использовать метод расширения для расширения Any(
… Contains)
выражение в выражение «или»:
public static class LinqKitExt { // using LINQKit
// keyFne - extract string key from row
// searchTerms - IEnumerable<string> where one must be contained by a row's key
// dbq.Where(r => searchTerms.Any(s => keyFne(r).Contains(s)))
public static IQueryable<T> WhereContainsAny<T>(this IQueryable<T> dbq, Expression<Func<T,string>> keyFne, IEnumerable<string> searchTerms) {
var pred = PredicateBuilder.New<T>();
foreach (var s in searchTerms)
pred = pred.Or(r => keyFne.Invoke(r).Contains(s));
return dbq.Where((Expression<Func<T,bool>>)pred.Expand());
}
}
(И 51 другой вариант, где/OrderBy[По убыванию] Любой / Все содержит/начинается с.)
Тогда вы можете использовать его следующим образом
var result = _context.Products
.WhereContainsAny(r => r.Name, searchParameters)
.ToList();
PS Преследуя другой ответ, я понял, что перенос теста на вызывающий объект устранил большинство вариантов:
// searchTerms - IEnumerable<TKey> where all must be in a row's key
// testFne(row,searchTerm) - test one of searchTerms against a row
// dbq.Where(r => searchTerms.Any(s => testFne(r,s)))
public static IQueryable<T> WhereAny<T,TKey>(this IQueryable<T> dbq, IEnumerable<TKey> searchTerms, Expression<Func<T, TKey, bool>> testFne) {
var pred = PredicateBuilder.New<T>();
foreach (var s in searchTerms)
pred = pred.Or(r => testFne.Invoke(r, s));
return dbq.Where((Expression<Func<T,bool>>)pred.Expand());
}
Затем вы просто вызываете:
var result = _context.Products
.WhereAny(searchParameters, (r,s) => r.Name.Contains(s))
.ToList();
Комментарии:
1. Работает как мечта! Спасибо! Мне просто любопытно, не было ли это поведение включено для Entity Framework (не Core) ранее? В любом случае, я пока придерживаюсь predicatebuilder! Большое спасибо!
2. @RKrogh Я полагаю, что вы можете быть правы, ядро EF (даже 5) потеряло много переводов из EF 6 в стремлении быть более последовательным (и часто менее полезным)…
3. @RKrogh К вашему сведению, я нашел способ сделать его более универсальным, передав тест вызывающей стороне; а также исправлена ошибка в
ExpressionStarter
обработке в исходном ответе4. Приятно! Спасибо, что вернулись ко мне. Мне нравится вторая версия. Проверит это. В чем заключалась ошибка? Я запускал его с тех пор, как вы его дали, и ничего не заметил
![]()
5. @RKrogh Не используя
var
withpred
, первый вызов.Or
or.And
не понял, что нужно удалить starter, и вместо этого по умолчанию используетсяfalse
который работает с.Or
, но не работает с.And
. (В основном компилятор вызывает неправильную версию метода.Or
or.And
.)