KrisSodroski
KrisSodroski

Reputation: 2842

LINQ expressions add unnecessary parentheses

I'm building a LINQ expression in C# like this:

public static Expression BuildEqualsExpression(
    ParameterExpression Parameter,
    PropertyInfo Property,
    ConstantExpression constant)
{
    Expression propertyExpression = Expression.Property(Parameter, Property);
    return Expression.Equal(propertyExpression, constant);
}

The returned Expression is

(r.Property == constant)

when what I want is

r.Property == constant

without the parentheses. My IQueryProvider does not treat these two statements equally. Does anyone know how I can get rid of the parentheses?

If I AND two of these together, I get

((r.Property1 == constant) And (r.Property2 == constant))

when I want

r.Property1 == constant And r.Property2 == constant

since they are not equivalent predicates.

Edit:

Thanks for your help. It turns out that in SQL server 2014, using this command:

OFFSET 0 ROWS FETCH NEXT 100 ROWS ONLY 

When the number of actual rows is < 100, this will cause the query to execute extremely slowly.

Upvotes: 1

Views: 1816

Answers (1)

Michael Liu
Michael Liu

Reputation: 55399

There are no LINQ expression nodes that explicitly represent parentheses. Instead, operator precedence and associativity are automatically inferred from the structure of the expression tree.

It seems that either you or the query provider is calling ToString() on the expression. Don't do this. The expression trees specification states:

Expression.ToString is for light weight debugging purposes only. [...]

ToString does not try to return semantically accurate C# or VB code in particular. We try to return terse strings loosely suggesting what an ET [expression tree] node contains for quick inspection only.

As Thomas Levesque notes in a comment, the best approach is for the query provider to work directly with expression trees instead of strings.

If the query provider only accepts strings, then you'll have to convert the expression to a string yourself. Your task will be complicated by the fact that extra parentheses are problematic, because you still need to insert parentheses to distinguish, say, (1 + 2) * 3 from 1 + (2 * 3).

Here's some code to get you started:

private static readonly Dictionary<ExpressionType, string> s_binaryOperators =
    new Dictionary<ExpressionType, string>
    {
        { ExpressionType.Equal, " == " },
        { ExpressionType.And, " AND " },
    };

public static void ToString(Expression expression, StringBuilder builder)
{
    switch (expression.NodeType)
    {
        case ExpressionType.Parameter:
            builder.Append(((ParameterExpression)expression).Name);
            break;

        case ExpressionType.Constant:
            builder.Append(((ConstantExpression)expression).Value);
            break;

        case ExpressionType.MemberAccess:
            var memberExpression = (MemberExpression)expression;
            // TODO: Add parentheses if memberExpression.Expression.NodeType
            // has lower precedence than the current expression.
            ToString(memberExpression.Expression, builder);
            builder.Append('.').Append(memberExpression.Member.Name);
            break;

        case ExpressionType.Equal:
        case ExpressionType.And:
            var binaryExpression = (BinaryExpression)expression;
            // TODO: Add parentheses if binaryExpression.Left.NodeType
            // has lower precedence than the current expression.
            ToString(binaryExpression.Left, builder);
            builder.Append(s_binaryOperators[expression.NodeType]);
            // TODO: Add parentheses if binaryExpression.Right.NodeType
            // has lower precedence than the current expression.
            ToString(binaryExpression.Right, builder);
            break;

        default:
            throw new NotImplementedException();
    }
}

Upvotes: 4

Related Questions