Как я могу использовать Linq, чтобы найти элементы с подходящими словами из строки через запятую? - PullRequest
0 голосов
/ 25 января 2020

Я пытаюсь написать запрос Linq для поиска элементов со строковым свойством Tags, содержащим хотя бы одно из слов в строке через запятую, queryTags.

Свойство Tags равно также строка, разделенная запятыми, и значение может быть «toy, ball, red, plasti c».

Строка поиска queryTags может быть, например, «red, ball». В этом случае элемент должен быть выбран, потому что совпадают и «красный», и «шар».

Это то, что у меня есть:

Модель

public class WebContentsImage
{
    public int Id { get; set; }
    public string Tags { get; set; } // E.g. "toy,ball,red,plastic"
    // some more properties
}

Запрос

List<WebContentsImage> Images = await db.WebContentsImages
    .Where(t => t.Tags.Any(queryTags.Contains))
    .ToListAsync()
    .ConfigureAwait(false);

Я не совсем понимаю, что должен делать запрос выше. Я только что скопировал его из этого ответа, надеясь, что он соответствует тому, что я пытаюсь сделать. В любом случае я получаю из этого исключение времени выполнения:

NotSupportedException: не удалось проанализировать выражение 't.Tags.Any (Convert (__ CreateDelegate_0, Func`2))': указанные аргументы не соответствуют Ожидаемые аргументы: Объект типа «System.Linq.Expressions.UnaryExpression» нельзя преобразовать в тип «System.Linq.Expressions.LambdaExpression».

Ранее у меня было следующее:

List<WebContentsImage> Images = await db.WebContentsImages
    .Where(t => t.Tags.Contains(queryTags.Split(",")))
    .ToListAsync()
    .ConfigureAwait(false);

... с ошибкой во время компиляции:

Аргумент 1: невозможно преобразовать строку [] в char

И это (что просто неправильно):

List<WebContentsImage> Images = await db.WebContentsImages
    .Where(t => t.Tags.Contains(queryTags, StringComparison.CurrentCultureIgnoreCase))
    .ToListAsync()
    .ConfigureAwait(false);

Ответы [ 2 ]

3 голосов
/ 25 января 2020

Кажется, вы используете БД, поэтому вам нужно помнить о том, что можно, а что нельзя конвертировать в SQL

. Вы можете добиться большего успеха с чем-то вроде:

var x = db.WebContentsImages;

foreach(string tag in queryTags.Split(','))
    x = x.Where(t => (',' + t.Tags + ',').Contains(',' + tag + ',', StringComparison.CurrentCultureIgnoreCase));


var list = x.ToListAsync();

Повторное где будет действовать совокупно и, мы надеемся, сгенерирует набор sql вроде:

SELECT * FROM table 
WHERE ','+tags+',' LIKE '%,red,%' AND
      ','+tags+',' LIKE '%,ball,%'

Я должен отметить, что это довольно ужасный c способ хранения данных в БД и попросить его вернуть вещи, основанные на строке LIKE .. теги должны действительно иметь свою собственную таблицу, а затем таблица-посредник отображает, какие вещи имеют какие теги

Вы путаетесь с текущей структурой, потому что строка имеет метод Contains, который возвращает значение true, если в этой строке существует подстрока, и Linq расширяет неперечисляемые коллекции, чтобы также иметь другой метод Contains, который сообщает, содержит ли коллекция конкретный элемент. Вы смешиваете два и запрашиваете строку Contains, чтобы сообщить, содержит ли строка массив et c. Вам необходимо все время использовать LINQ Contains, что означает разделение ваших тегов по запятой, а затем запрос о том, содержит ли результирующий массив строк весь массив строк, полученный в результате разбиения queryTags на запятую

Проблема в том, что, хотя это может произойти на клиенте, я сомневаюсь, что ваш ORM сможет выполнить разделение на стороне сервера, что означает, что ему придется перетаскивать всю таблицу базы данных на клиент. Вот почему я пошел другим путем и конвертировал все для использования String Contains в надежде, что он станет кучей LIKE AND на сервере ..

Было бы лучше не хранить данные таким образом

Если бы теги и теги запроса были чем-то вроде IEnumerable из строки, у вас больше шансов сказать

products.Where(
  product => queryTags.All(queryTag => product.Tags.Contains(queryTag)
)

В Engli sh это «для всех продуктов, фильтруйте только продукты, в которых все теги запроса присутствуют в продукте. Список тегов

Таким образом, вы можете манипулировать текущими данными; сделать queryTags строкой, полученной в результате разделения строки запроса на запятую, и переименовать строку csv Tags в TagsCsv и создать новое свойство с именем Tags, которое возвращает TagsCsv.Split (','), но я полностью ожидаю, что он должен быть выполнен на клиент, не в дб

var queryTags = “red,ball”.Split(',');

//horrific, for illustration purposes only
class Product{
  String TagsCsv = “red,plastic,round,ball”;
  String[] Tags { get { return TagsCsv.Split(',') } }
}
0 голосов
/ 25 января 2020

это должно сделать

List<WebContentsImage> Images = await db.WebContentsImages
    .Where(i => i.Tag.Split(",").Any(t => queryTags.Split(",").Contains(t)));
    .ToListAsync()
    .ConfigureAwait(false);

вы были почти там

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...