【问题标题】:How to dynamically add OR operator to WHERE clause in LINQ如何在 LINQ 中将 OR 运算符动态添加到 WHERE 子句
【发布时间】:2010-10-21 09:45:37
【问题描述】:

我有一个可变大小的字符串数组,我试图以编程方式循环遍历数组并匹配表中的所有行,其中“标签”列至少包含数组中的一个字符串。这是一些伪代码:

 IQueryable<Songs> allSongMatches = musicDb.Songs; // all rows in the table

我可以轻松地在一组固定的字符串上查询此表过滤,如下所示:

 allSongMatches=allSongMatches.Where(SongsVar => SongsVar.Tags.Contains("foo1") || SongsVar.Tags.Contains("foo2") || SongsVar.Tags.Contains("foo3"));

但是,这不起作用(我收到以下错误:“无法将带有语句体的 lambda 表达式转换为表达式树”)

 allSongMatches = allSongMatches.Where(SongsVar =>
     {
       bool retVal = false;
       foreach(string str in strArray)
       {
         retVal = retVal || SongsVar.Tags.Contains(str);
       }
       return retVal;
     });

谁能告诉我正确的策略来完成这个?我对 LINQ 的世界还是新手 :-)

【问题讨论】:

    标签: linq linq-to-sql dynamic


    【解决方案1】:

    您可以使用PredicateBuilder 类:

    var searchPredicate = PredicateBuilder.False<Songs>();
    
    foreach(string str in strArray)
    {
       var closureVariable = str; // See the link below for the reason
       searchPredicate = 
         searchPredicate.Or(SongsVar => SongsVar.Tags.Contains(closureVariable));
    }
    
    var allSongMatches = db.Songs.Where(searchPredicate);
    

    LinqToSql strange behaviour

    【讨论】:

    • 感谢 Mehrdad,您的解决方案在创纪录的时间内出现并且效果非常好!我不知道 PredicateBuilder 类。多么方便的功能!我也很感谢您对使用临时变量来保存字符串的评论......这会让我发疯!维克多
    • 我对创建 var searchPredicate 的 False 表达式感到困惑,这需要为 false 吗?或者还有什么适合的? (谈论 PredicateBuilder.False() )
    • 它必须是 false,因为构造的查询需要看起来像“false || contains x || contains y || ...”
    【解决方案2】:

    我最近创建了一个用于创建字符串搜索的扩展方法,它也允许OR 搜索。关于here的博客

    我还将它创建为可以安装的 nuget 包:

    http://www.nuget.org/packages/NinjaNye.SearchExtensions/

    安装后,您将能够执行以下操作

    var result = db.Songs.Search(s => s.Tags, strArray);
    

    如果您想创建自己的版本以允许上述操作,您需要执行以下操作:

    public static class QueryableExtensions  
    {  
        public static IQueryable<T> Search<T>(this IQueryable<T> source, Expression<Func<T, string>> stringProperty, params string[] searchTerms)  
        {  
            if (!searchTerms.Any())  
            {  
                return source;  
            }  
    
            Expression orExpression = null;  
            foreach (var searchTerm in searchTerms)  
            {  
                //Create expression to represent x.[property].Contains(searchTerm)  
                var searchTermExpression = Expression.Constant(searchTerm);  
                var containsExpression = BuildContainsExpression(stringProperty, searchTermExpression);  
    
                orExpression = BuildOrExpression(orExpression, containsExpression);  
            }  
    
            var completeExpression = Expression.Lambda<Func<T, bool>>(orExpression, stringProperty.Parameters);  
            return source.Where(completeExpression);  
        }  
    
        private static Expression BuildOrExpression(Expression existingExpression, Expression expressionToAdd)  
        {  
            if (existingExpression == null)  
            {  
                return expressionToAdd;  
            }  
    
            //Build 'OR' expression for each property  
            return Expression.OrElse(existingExpression, expressionToAdd);  
        }  
    }
    

    或者,查看NinjaNye.SearchExtensions 的 github 项目,因为它有其他选项,并且经过一些重构以允许其他组合

    【讨论】:

      【解决方案3】:

      还有另一种更简单的方法可以完成此操作。 ScottGu 的博客详细介绍了我过去发现非常有用的动态 linq 库。本质上,它从您传入的字符串生成查询。这是您编写的代码示例:

      Dim Northwind As New NorthwindDataContext
      
      Dim query = Northwind.Products _
                           .Where("CategoryID=2 AND UnitPrice>3") _
                           .OrderBy("SupplierId")
      
      Gridview1.DataSource = query
      Gridview1.DataBind()
      

      更多信息可以在 scottgu 的博客here.找到

      【讨论】:

        【解决方案4】:

        要么自己建立一个Expression&lt;T&gt;,要么看看不同的路线。

        假设 possibleTags 是标签的集合,您可以使用闭包和连接来查找匹配项。这应该会在 possibleTags 中找到至少有一个标签的任何歌曲:

        allSongMatches = allSongMatches.Where(s => (select t from s.Tags
                                                    join tt from possibleTags
                                                        on t == tt
                                                    select t).Count() > 0)
        

        【讨论】:

        • 嗨,理查德,感谢您的反馈。出于某种原因,VS 不喜欢您提出的代码的语法......我是否遗漏了一些明显的东西?
        • 很可能有错误...需要一点时间来整理数据/测试工具来确认。但是在内部理解表达式中添加了 select 子句......这是必需的(哎呀)。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2010-09-15
        • 1970-01-01
        • 2014-12-19
        • 1970-01-01
        • 2014-10-15
        • 1970-01-01
        相关资源
        最近更新 更多