Построение «плоских», а не «древовидных» выражений LINQ - PullRequest
5 голосов
/ 30 мая 2010

Я использую некоторый код (доступен здесь на MSDN) для динамического построения выражений LINQ, содержащих несколько «предложений» OR.

Соответствующий код

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 ]

6 голосов
/ 31 мая 2010

Вам нужно изменить генерацию так, чтобы она строила сбалансированное дерево вместо последовательности 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 голос
/ 31 мая 2010

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

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

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