Есть ли способ динамически генерировать несколько лайков из массива строковых значений в EF? - PullRequest
0 голосов
/ 12 июня 2019

Я установил текстовое поле поиска, в котором при поиске каждое слово подбирается индивидуально и выполняется поиск по полю с помощью Contains.

Существует ли способ поиска в массиве строк с помощью Contains?

//Keep in mind that the array would be generated dynamically through textbox
string[] searchWords = { "hello", "world", "today" };

var articles = _swmDbContext.Articles
                        .Include(c => c.Category)
                        .Where(a => a.Title.Contains(searchWords));

searchWords явно не работает, но пытается показать, чего я хочу достичь.searchWords [0] работает, потому что это всего лишь одно слово.

Я также попробовал ниже, как предложено в других ссылках, но теперь предложение WHERE не отображается в запросе, когда я запускаю отладчик или профилировщик:

`var articles = _swmDbContext.Articles
                 .Include(c => c.Category)
                 .Where(a => searchWords.Any(w => a.Title.Contains(w)));

`

1 Ответ

2 голосов
/ 13 июня 2019

Похоже, что Entity Framework Core не переводит .Any и .All с .Contains в приведенном выше запросе в операторы SQL.Вместо этого он загружает все остальные совпадающие данные и выполняет поиск в памяти.

Если вы хотите найти Статьи, которые содержат все поисковых слов в заголовке, вы можете динамически добавить .Where условия (Iимел тестовую базу данных с персонами и полем «Комментарий»):

var query = (IQueryable<Person>)dbContext.Persons
    .Include(p => p.TaxIdentificationNumber);

foreach (var searchWord in searchWords)
{
    query = query.Where(p => p.Comment.Contains(searchWord));
}

var persons = query.ToList();

Но если вы хотите найти статьи, содержащие любой поисковых слов, тогда вам понадобится OR в.Where предложение.

Если написать это вручную, это будет выглядеть так:

.Where(p => p.Comment.Contains(searchWords[0]) || p.Comment.Contains(searchWords[1]))

Но вы можете динамически построить выражение:

Expression<Func<Person, bool>> e1 = p => p.Comment.Contains(searchWords[0]);
Expression<Func<Person, bool>> e2 = p => p.Comment.Contains(searchWords[1]);
Expression<Func<Person, bool>> e3 = p => p.Comment.Contains(searchWords[2]);
var orExpression1 = Expression.OrElse(e1.Body, Expression.Invoke(e2, e1.Parameters[0]));
var orExpression2 = Expression.OrElse(orExpression1, Expression.Invoke(e3, e1.Parameters[0]));
var finalExpression = Expression.Lambda<Func<Person, bool>>(orExpression2, e1.Parameters);  

и использовать его следующим образом:это:

var persons = dbContext.Persons.Where(finalExpression).ToList();

как функция:

Expression<Func<Person, bool>> BuildOrSearchExpression(string[] searchWords)
{
    // searchWords must not be null or empty

    var expressions = searchWords.Select(s => (Expression<Func<Person, bool>>)(p => p.Comment.Contains(s))).ToList();

    if (expressions.Count == 1) return expressions[0];

    var orExpression = expressions.Skip(2).Aggregate(
        Expression.OrElse(expressions[0].Body, Expression.Invoke(expressions[1], expressions[0].Parameters[0])),
        (x, y) => Expression.OrElse(x, Expression.Invoke(y, expressions[0].Parameters[0])));

    return Expression.Lambda<Func<Person, bool>>(orExpression, expressions[0].Parameters);
}   

и использовать его

var persons = dbContext.Persons
    .Include(p => p.TaxIdentificationNumber)
    .Where(BuildOrSearchExpression(searchWords))
    .ToList();  

Если вы поменяете .OrElse на .AndAlso все поисковые словадолжен быть найден как с несколькими пунктами .where.

Когда я провел какое-то исследование, я также наткнулся на PredicatedBuilder http://www.albahari.com/nutshell/predicatebuilder.aspx и это SearchExtension https://stackoverflow.com/a/31682364/5550687. Но я не пробовал их и не знаю, работают ли они с EF Core.

...