【问题标题】:Using contains() in LINQ to SQL在 LINQ to SQL 中使用 contains()
【发布时间】:2011-01-23 01:33:40
【问题描述】:

我正在尝试使用 linq-to-sql 在应用程序中实现一个非常基本的关键字搜索。我的搜索词在一个字符串数组中,每个数组项都是一个单词,我想找到包含搜索词的行。我不介意它们是否只包含搜索词(很可能,它们会),但所有搜索词都必须存在。

理想情况下,我想要类似于下面的 sn-p 的东西,但我知道这行不通。另外,我查看了this question here,但该问题的作者似乎满足于以相反的方式做事(query.Contains(part.partName)),这对我不起作用。

public IQueryable<Part> SearchForParts(string[] query)
{
    return from part in db.Parts
           where part.partName.Contains(query)
           select part;
}

我怎样才能重写这个查询,使它能够满足我的需要?

【问题讨论】:

  • 试着想想你想要做什么。这真的没有意义。如何将数组与字符串匹配?您至少需要告诉我们 partName 的样子。

标签: c# linq-to-sql contains keyword-search


【解决方案1】:

一个更简单更正确的解决方案(然后是leppie的):

public IQueryable<Part> SearchForParts(string[] query)
{
    var q = db.Parts.AsQueryable(); 

    foreach (string qs in query)
    {
        q = q.Where(x => x.partName.Contains(qs));
    }

    return q;
}

只要partName 是字符串(或字符串的 SQL 等效项),这将起作用。

需要注意的重要一点是partName.Contains(qs)query.Contains(partName) 不同。
使用partName.Contains(qs),在partName 中搜索任何出现的qs。生成的 SQL 将是等效的(其中 qs 的值):

select * from Parts where partName like '%<qs>%';

还要注意的是StartsWithEndsWith,它们与Contains 相似,但在特定位置查找字符串。

query.Contains(partName) 与 SQL in 命令相同。生成的 SQL 将等效于(其中 query[0] 的值,query[1] 的值, 是查询数组中的最后一个值):

select * from Parts where partName in ( <query0>, <query1>, ..., <queryN> );

更新:
同样重要的是要注意,leppie 的答案在将通配符添加到like 语句之前不会对其进行转义。这不是Contains 解决方案的问题,因为Linq 将在发送查询之前对其进行转义。 SqlMethods.Like 解决方案的转义版本是:

public IQueryable<Part> SearchForParts(string[] query)
{
    var q = db.Parts.AsQueryable(); 

    foreach (var qs in query)
    {
        string escaped_bs = qs.Replace("/", "//"),
            escaped_us = escaped_bs.Replace("_", "/_"),
            escaped_p = escaped_us.Replace("%", "/%"),
            escaped_br = escaped_p.Replace("[", "/["),
            likestr = string.Format("%{0}%", escaped_br);

        q = q.Where(x => SqlMethods.Like(x.partName, likestr, '/'));
    }

    return q;
}

您不必担心 ',因为 Linq 会为您避免这种情况。

【讨论】:

  • 您分配了 escaped_p 但从不使用它。错字?
  • @PatrickM 是的,我忘记在复制/粘贴后更改一些代码。现在已经修复了。
  • 在我的测试中,我返回了一个IQueryable&lt;IMyInterface&gt;,然后我将Contains 应用到,所以我将不得不看看这是否影响了代码(即意外地将其转换为 LINQ 到实体)。我现在将删除我之前的评论。
  • @TrueBlueAussie 我遇到了一些问题,即 LINQ 语句中的顺序错误会导致查询在本地计算机上运行。例如,如果您添加一个 order by 子句,然后添加一个 where 过滤器,它似乎总是降低有序数据,然后应用您的 where 过滤器。如果包含转换为 SQL,那么它将使用 LIKE 和 '%&lt;yourText&gt;%'。如果您不能保证 LINQ 将在 SQL 中运行,那么只有您使用 ToLower 的解决方案才能工作。
【解决方案2】:

使用NinjaNye.SearchExtension nuget 包可以让您轻松执行此搜索:

string[] terms = new[]{"search", "term", "collection"};
var result = db.Parts.Search(terms, p => p.PartName);

您还可以搜索多个字符串属性

var result = db.Parts.Search(terms, p => p.PartName, p.PartDescription);

或者执行一个RankedSearch,它返回IQueryable&lt;IRanked&lt;T&gt;&gt;,它只包含一个显示搜索词出现次数的属性:

//Perform search and rank results by the most hits
var result = db.Parts.RankedSearch(terms, p => p.PartName, p.PartDescription)
                     .OrderByDescending(r = r.Hits);

项目 GitHub 页面上有更详尽的指南:https://github.com/ninjanye/SearchExtensions

希望这对未来的访客有所帮助

【讨论】:

    【解决方案3】:

    我觉得这有点简单并且对我有用:

    string[] product = products.Split(','); 
    using (var context = new ProjectTrackerEntities()) 
    { var result = from part in context.DBAudits where product.Contains(part.TableName) select part; }
    

    【讨论】:

      【解决方案4】:

      看到其他尝试让我很难过:(

      public IQueryable<Part> SearchForParts(string[] query)
      {
        var q = db.Parts.AsQueryable(); 
      
        foreach (var qs in query)
        { 
          var likestr = string.Format("%{0}%", qs);
          q = q.Where(x => SqlMethods.Like(x.partName, likestr));
        }
      
        return q;
      }
      

      假设:

      • partName 看起来像:“ABC 123 XYZ”

      • 查询是 { "ABC", "123", "XY" }

      【讨论】:

      • 这相当于我的回答,只是你没有转义通配符('_'、'%'、'[')。
      【解决方案5】:

      请试试这个:

      public IQueryable<Part> SearchForParts(string[] query)
      {
          return from part in db.Parts
                 where Search(part.Name,query)
                 select part;
      }
      
      public bool Search(string partName,string[] query)
      {
          for (int i = 0; i < query.Length; i++)
          {
              if(partName.Contains(query[i]))
                 return true;
          }
      
          return false;
      }
      

      【讨论】:

      • 您的 Search 方法对 SQL 不可用 - 除非您通过在 db.Parts 之后添加 .ToList() 来获取每个部分,否则它将无法正常工作
      【解决方案6】:

      你可以这样写

      var result = db.Parts.Where(p => query.All(q => p.partName.Contains(q)));
      

      【讨论】:

      • 这样做会导致错误“Local sequence cannot be used in LINQ to SQL implementation of query operator except the Contains() operator.” - 同下。跨度>
      • 这对我来说在类似的查询 MVC 2/.NET 4 中效果很好。该问题的接受答案使用了我在项目中找不到命名空间的 SqlMethods - 已弃用?跨度>
      • SqlMethods 在 System.Data.Linq.SqlClient 命名空间中。仅在 Linq to SQL 实现中使用 SqlMethods,您需要参考 System.Data.Linq 程序集
      【解决方案7】:

      你可以试试:

      public IQueryable<Part> SearchForParts(string[] query)
      {
          return from part in db.Parts
                 where query.All(term => part.partName.Contains(term))
                 select part;
      }
      

      但是,我不确定 LINQ to SQL 是否能够将其转换为 T-SQL。另一种选择是:

      public IQueryable<Part> SearchForParts(string[] query)
      {
          var result = from part in db.Parts
                       select part;
      
          foreach(var term in query)
          {
              result = from part in result
                       where part.partName.Contains(term)
                       select part;
          }
      
          return result;
      }
      

      它不那么漂亮,但它应该可以工作。您将在 where 子句中获得包含大量 ANDs 的查询。

      【讨论】:

      • 这样做会导致错误“Local sequence cannot be used in LINQ to SQL implementation of query operator except the Contains() operator。”
      • @casperOne:传递给All 的表达式使用Contains 方法,而不是等于运算符,这意味着将测试部件名称以查看它是否包含查询词而不是完全等于它。至于使用Any,问题指出结果必须匹配所有搜索词,而不仅仅是其中一个(这是任何人都会做的)。
      • @a_m0d:我确实说过我不确定它是否会转变。试试第二个建议,应该会有更好的结果。
      • 说实话,我并不太在意它们是否都在那里,但我觉得如果它们都在那里,搜索会更好,并且可能更容易实现好吧。
      • 是的,第二个建议确实有效,但我不太喜欢像这样将它全部加载到内存中然后搜索它的想法。理想情况下,我会让数据库搜索我,但也许这只是我过早的优化。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-03-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-09-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多