构建'flat'而不是'tree'LINQ表达式

时间:2010-05-30 19:51:20

标签: .net linq wcf-data-services

我正在使用一些代码(在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)

如何修改表达式构建代码来执行此操作?

2 个答案:

答案 0 :(得分:6)

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

 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);

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

答案 1 :(得分:1)

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

然而,更重要的是:如果您只想查看某个特定集合中是否存在ID,为什么不使用以下内容:

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