Я использую некоторый код (доступный здесь на MSDN) для динамичного создания выражений LINQ, содержащих несколько ИЛИ 'пункты'.
Соответствующие нормы
var equals = values.Select(value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
var body = equals.Aggregate((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)
Как я изменил бы строительные нормы и правила выражения, чтобы сделать это?
Вам необходимо изменить генерацию так, чтобы она строила сбалансированное дерево вместо последовательности 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);
Я не пробовал код, поэтому могут быть некоторые незначительные ошибки, но он должен показать идею.
Если это действительно LINQ to Objects в соответствии с вашими тегами, зачем вы вообще строите деревья выражений? Вы можете очень легко использовать делегатов, и у них не будет ограничения на рекурсию.
Однако ближе к делу: если вы просто хотите узнать, есть ли идентификатор в какой-то конкретной коллекции, почему бы вам не использовать что-то вроде:
var query = from item in source
where idCollection.Contains(item.Id)
...