【问题标题】:How can I use Linq to find items with matching words from a comma separated string?如何使用 Linq 从逗号分隔的字符串中查找匹配单词的项目?
【发布时间】:2020-01-25 14:09:41
【问题描述】:

我正在尝试编写一个 Linq 查询来查找具有字符串属性 Tags 的项目,其中至少包含一个以逗号分隔的字符串 queryTags 中的单词。

属性Tags也是逗号分隔的字符串,值可以是“toy,ball,red,plastic”。

搜索字符串queryTags 可以是例如“red,ball”。在这种情况下,应该选择该项目,因为“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);

我真的不明白上面的查询应该做什么。我刚刚从this 答案中复制了它,希望它与我想要做的一致。无论如何,我从中得到了这个运行时异常:

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:无法从 string[] 转换为 char

还有这个(这是完全错误的):

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

【问题讨论】:

  • 如果标签和查询标签是字符串集合而不是 CSV 单个字符串,您的生活会更轻松。您愿意更改此设置还是必须保留 csv?
  • 我同意。我将把我的模型改为有一个相关的标签表。

标签: c# linq asp.net-core entity-framework-core


【解决方案1】:

您似乎在使用 db,因此您需要注意哪些可以转换为 SQL,哪些不能转换为 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();

重复的where会累积作用,并希望生成一组sql,如:

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

我应该指出,尽管这是将数据存储在数据库中并要求它根据字符串 LIKE 返回内容的一种相当可怕的方式。标签应该真的有自己的表,然后中间人表映射事物所具有的内容哪些标签

您对当前结构感到困惑,因为 string 有一个 Contains 方法,如果该字符串中存在子字符串,则该方法返回 true,并且 Linq 扩展 ienumerable 集合也有一个不同的 Contains 方法,该方法告诉集合是否包含特定元素.您将两者混合并要求字符串包含以报告字符串是否包含数组等。您需要一直使用 LINQ 包含,这意味着将标签拆分为逗号,然后询问生成的字符串数组是否包含用逗号分割queryTags产生的所有字符串数组

问题是,虽然这可能发生在客户端,但我怀疑您的 ORM 是否能够在服务器端执行拆分,这意味着它必须将整个数据库表拖到客户端。这就是为什么我采取另一种方式并将所有内容转换为使用 String Contains 的原因,希望它会成为服务器上的一堆 LIKE AND ..

最好不要这样存储数据

如果 Tags 和 queryTags 是某种 IEnumerable 字符串,那么您将更有可能说

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

在英语中,这是“对于所有产品,仅过滤到所有 queryTag 都存在于 product.Tags 列表中的产品

您可以这样操作您当前的数据;通过用逗号分割查询字符串使 queryTags 成为字符串结果,并将您的 Tags csv 字符串重命名为 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(',') } }
}

【讨论】:

    【解决方案2】:

    应该这样做

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

    你快到了

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-26
      • 1970-01-01
      • 1970-01-01
      • 2013-01-08
      • 1970-01-01
      相关资源
      最近更新 更多