【问题标题】:Building 'flat' rather than 'tree' LINQ expressions构建“平面”而不是“树”LINQ 表达式
【发布时间】:2010-10-04 03:55:07
【问题描述】:

我正在使用一些代码(可在 MSDN 上找到 here)来动态构建包含多个 OR“子句”的 LINQ 表达式。

相关代码是

var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));

var body = equals.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));

这会生成一个如下所示的 LINQ 表达式:

(((((ID = 5) OR (ID = 4)) OR (ID = 3)) OR (ID = 2)) OR (ID = 1))

我在使用这个表达式时达到了递归限制 (100),所以我想生成一个如下所示的表达式:

(ID = 5) OR (ID = 4) OR (ID = 3) OR (ID = 2) OR (ID = 1)

我将如何修改表达式构建代码来做到这一点?

【问题讨论】:

    标签: .net linq wcf-data-services


    【解决方案1】:

    您需要修改生成,以便它构建平衡树而不是 ORs 序列,其中左子树是单个表达式,右子树包含所有剩余元素。图形化:

     Your code               Better
     ---------              --------
        OR                     OR
     #1    OR              OR      OR
         #2  OR          #1  #2  #3  #4
           #3  #4
    

    如您所见,即使在这种简单的情况下,更好的方法也没有那么深(递归嵌套)。生成更好的表达式树的代码可以写成 C# 中的递归方法:

    Expression GenerateTree(List<Expression> exprs, int start, int end) {
      // End of the recursive processing - return single element
      if (start == end) return exprs[start];
    
      // Split the list between two parts of (roughly the same size)
      var mid = start + (end - start)/2;
      // Process the two parts recursively and join them using OR
      var left = GenerateTree(exprs, start, mid);
      var right = GenerateTree(exprs, mid+1, end);
      return Expression.Or(left, right);
    }
    
    // Then call it like this:
    var equalsList = equals.ToList();
    var body = GenerateTree(equalsList, 0, equalsList.Length);
    

    我没有尝试代码,所以可能会有一些小错误,但它应该显示这个想法。

    【讨论】:

    • 微小的变化 - 用 equalsList.Count-1 替换 equalsList.Length - 它工作得很好。谢谢。
    【解决方案2】:

    如果根据您的标签,这确实是 LINQ to Objects,那么您为什么要构建表达式树呢?您可以非常轻松地使用委托,并且它们没有递归限制。

    但是,更重要的是:如果您只想查看某个 ID 是否在某个特定集合中,为什么不使用类似的东西:

    var query = from item in source
                where idCollection.Contains(item.Id)
                ...
    

    【讨论】:

    • 抱歉,我的标签不正确。我正在使用 WCF 数据服务,这是遇到递归限制的地方。
    • @Ian:WCF 数据服务不允许您使用 Contains 吗?这仍然是 IMO 的首选方法...
    • 它不在 .NET 3.5 中。它无法将 Contains 转换为 URI 语法。
    • LinqToSql 也存在 Contains 和 ANSI 字符串的问题,所以我也需要使用 OR 来处理。
    • @Giedrius:“ANSI 字符串”到底是什么意思?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-28
    • 1970-01-01
    相关资源
    最近更新 更多