Я согласен с ОП. У меня было то же исключение StackOverflowException с использованием метода BuildContainsExpression, которое опубликовали многие люди (в моем выражении было 6000 OR). Я изменил BuildContainsExpression для создания сбалансированного дерева (глубина = O (log (N))). В случае, если это может быть кому-то полезно, вот оно:
public static Expression<System.Func<TElement, bool>> BuildContainsExpression<TElement, TValue>(
Expression<System.Func<TElement, TValue>> valueSelector, IEnumerable<TValue> values)
{
if (null == valueSelector) { throw new ArgumentNullException("valueSelector"); }
if (null == values) { throw new ArgumentNullException("values"); }
ParameterExpression p = valueSelector.Parameters.Single();
// p => valueSelector(p) == values[0] || valueSelector(p) == ...
if (!values.Any())
{
return e => false;
}
var equals = values.Select(
value => (Expression)Expression.Equal(valueSelector.Body, Expression.Constant(value, typeof(TValue))));
//The use of ToArray here is very important for performance reasons.
var body = GetOrExpr(equals.ToArray());
return Expression.Lambda<System.Func<TElement, bool>>(body, p);
}
private static Expression GetOrExpr(IEnumerable<Expression> exprList)
{
return GetOrExpr(exprList, 0, exprList.Count() - 1);
}
private static Expression GetOrExpr(IEnumerable<Expression> exprList, int startIndex, int endIndex)
{
if (startIndex == endIndex)
{
return exprList.ElementAt(startIndex);
}
else
{
int lhsStart = startIndex;
int lhsEnd = (startIndex + endIndex - 1) / 2;
int rhsStart = lhsEnd + 1;
int rhsEnd = endIndex;
return Expression.Or(GetOrExpr(exprList, lhsStart, lhsEnd), GetOrExpr(exprList, rhsStart, rhsEnd));
}
}
Ключом к изменению является метод GetOrExpr, который заменяет использование Aggregate в исходной версии. GetOrExpr рекурсивно разделяет список предикатов пополам, чтобы создать «левую и правую стороны», а затем создает выражение (lhs ИЛИ rhs). Пример использования будет примерно таким:
var customerIds = Enumerable.Range(1, 5);
Expression<Func<Customer, bool>> containsExpr = BuildContainsExpression<Customer, int>(c => c.CustomerId, customerIds);
Console.WriteLine(containsExpr);
Это генерирует выражение, подобное этому:
c => (((c.CustomerId = 1) Or (c.CustomerId = 2)) Or ((c.CustomerId = 3) Or ((c.CustomerId = 4) Or (c.CustomerId = 5))))