【问题标题】:LINQ to Entities: Why can't I use Split method as condition?LINQ to Entities:为什么我不能使用拆分方法作为条件?
【发布时间】:2009-08-30 19:36:28
【问题描述】:

我有以下 LINQ 查询:

var aKeyword = "ACT";
var results = from a in db.Activities
              where a.Keywords.Split(',').Contains(aKeyword) == true
              select a;

关键字是一个逗号分隔的字段。

每次我运行此查询时都会收到以下错误:

"LINQ to Entities 无法识别方法 'Boolean Contains[String](System.Collections.Generic.IEnumerable`1[System.String], System.String)' 方法,并且无法翻译此方法到商店表达式中。”

我正在尝试做的事情的替代方法是什么?

【问题讨论】:

  • 拜托,真的没有理由将布尔运算的结果与 true 进行比较。只需使用返回的布尔值。
  • 显然......我这样做是因为我使用的是 LINQ,它也不喜欢它自己的 Contains!

标签: .net linq linq-to-entities string-split


【解决方案1】:

您的问题是 LINQ-to-Entites 必须将您提供的所有内容转换为 SQL 以发送到数据库。

如果这确实是您需要做的,您将不得不强制 LINQ-to-Entities 拉回所有数据和 LINQ-to-Objects 以评估条件。

例如:

var aKeyword = "ACT";
var results = from a in db.Activities.ToList()
              where a.Keywords.Split(',').Contains(aKeyword) == true
              select a;

但请注意,这将拉回活动表中的所有对象。另一种方法可能是让数据库做一些初始过滤,然后过滤剩下的部分:

var aKeyword = "ACT";
var results = (from a in db.Activities
              where a.Keywords.Contains(aKeyword)
              select a).ToList().Where(a => a.KeyWords.Split(',').Contains(aKeyword));

这将使 LINQ-to-Entities 执行它所理解的过滤器(string.Contains 成为类似查询),该过滤器将过滤掉一些数据,然后通过 LINQ-to-Objects 应用您想要的真正过滤器对象回来。 ToList() 调用强制 LINQ-to-Entities 运行查询并构建对象,从而允许 LINQ-to-Objects 成为执行查询第二部分的引擎。

【讨论】:

  • @James - 是的,前者会花费你相当多的性能。后者较少(它需要表/索引扫描,但只会返回候选行)。如果这是您将经常运行的东西,我建议将关键字存储在数据库预拆分中(即,每个活动每个关键字一行的表格——Yannick 的建议)。然后,您可以将其编写为数据库可以很好地处理的查询。
  • 是的,我同意,我决定采用 Yannick 的建议!
  • 当使用这种方法时,我建议只做两个匹配,所以所有的处理仍然发生在服务器端: where a.Keywords.Contains("," + aKeyword) || a.Keywords.Contains(aKeyword + ",")
  • @Yannick - 这不会处理所有问题 - 它仍然会匹配以相关关键字开头/结尾的关键字,并且您还需要添加 || a.Keywords == aKeyword。您仍然需要后置过滤器。不过,单独的表格是真正可行的方法。
【解决方案2】:

针对您对大型数据集的性能考虑:

您将在客户端上进行非索引通配符字符串匹配,所以是的,会有性能损失。

在一个表格字段中有多个关键字是有原因的吗?您可以将其标准化,以获得一个 ActivityKeywords 表,其中对于每个 Activity,您都有许多关键字记录。

Activity(activity_id, ... /* 移除关键字字段 */) ---> ActivityKeywords(activity_id, keyword_id) ---> 关键字(keyword_id, value)

查看非第一范式:http://en.wikipedia.org/wiki/Database_normalization

编辑:此外,即使您坚持使用一列,也有一种方法可以在服务器端执行所有操作(如果您有严格的语法:'keyword1,keyword2,...,keywordN'):

var aKeyword = "ACT";
var results = (from a in db.Activities
              where a.Keywords.Contains("," + aKeyword) || a.Keywords.Contains(aKeyword + ",")
              select a;

【讨论】:

  • 真的只是为了方便使用。大多数活动只有几个关键字,因此我认为不需要另一个表。但是,我实际上从未想过按照您的建议去做……这可能会变得更好!谢谢。
  • 我确实对关键字有严格的语法,正如您所建议的那样,即关键字、关键字、关键字。但是,我决定和另一张桌子一起去。谢谢。
  • 非常聪明。但是要小心,在比较之前总是修剪你的财产
【解决方案3】:

我的猜测是您调用拆分的方式。它应该需要一个数组。也许它正在发现 Linq 中的另一个拆分并给您一个不寻常的错误:

这适用于 Linq to Objects:

 var dataStore = new List<string>
                    {
                        "foo,bar,zoo",
                        "yelp,foo",
                        "fred",
                        ""
                    };
 var results = from a in dataStore
               where a.Split(new[] {','}).Contains("foo")
               select a;

 foreach (var result in results)
 {
     Console.WriteLine("Match: {0}", result);
 }

输出以下内容:

Match: foo,bar,zoo
Match: yelp,foo

其实,仔细想想,你还需要拆分吗? a.Contains("foo") 对你来说可能就足够了(除非你不想打 foobar)。

【讨论】:

  • 是的 defo 需要拆分(如您所述)关键字可以是其他关键字的一部分。需要完全匹配。
  • 谢谢雷,你让我意识到我可以省略分裂!
【解决方案4】:

您可能希望look at this question about L2E and .Contains 寻求比在过滤客户端之前猜测超集更有效的解决方案。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-01-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多