2010-05-30 33 views
5

我使用一些代碼(MSDN上可用的here)來動態地生成包含多個OR'子句'的LINQ表達式。建立'平'而不是'樹'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)使用這個表達式時,所以我'd想要生成如下表達式:

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

如何修改前按建設代碼來做到這一點?

回答

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

我沒有嘗試的代碼,所以有可能會出現一些小錯誤,但它應該顯示的想法。

+0

微小的變化 - 用equalsList.Count-1替換equalsList.Length - 它完美的工作。謝謝。 – 2010-06-01 23:54:36

1

如果這是根據您的標籤真正LINQ到對象,爲什麼你建立表情樹呢?您可以非常輕鬆地使用委託,並且它們不會有遞歸限制。

然而,更重要的一點:如果你只是想看看的ID是否是在某些特定集合,你爲什麼不使用類似:

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

道歉,我的標籤是不正確的。我正在使用WCF數據服務,這是遇到遞歸限制的地方。 – 2010-05-30 20:29:38

+0

@Ian:WCF數據服務不允許你使用Contains嗎?這仍然是首選方法IMO ... – 2010-05-30 21:14:37

+0

它不在.NET 3.5中。它不能將Contains轉換爲URI語法。 – 2010-05-30 22:20:40