Создание 'плоских' а не 'древовидных' выражений LINQ

Я использую некоторый код (доступный здесь на 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)

Как я изменил бы строительные нормы и правила выражения, чтобы сделать это?

5
задан marc_s 3 October 2010 в 19:55
поделиться

2 ответа

Вам необходимо изменить генерацию так, чтобы она строила сбалансированное дерево вместо последовательности 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);

Я не пробовал код, поэтому могут быть некоторые незначительные ошибки, но он должен показать идею.

6
ответ дан 14 December 2019 в 08:43
поделиться

Если это действительно LINQ to Objects в соответствии с вашими тегами, зачем вы вообще строите деревья выражений? Вы можете очень легко использовать делегатов, и у них не будет ограничения на рекурсию.

Однако ближе к делу: если вы просто хотите узнать, есть ли идентификатор в какой-то конкретной коллекции, почему бы вам не использовать что-то вроде:

var query = from item in source
            where idCollection.Contains(item.Id)
            ...
1
ответ дан 14 December 2019 в 08:43
поделиться
Другие вопросы по тегам:

Похожие вопросы: