Use string.Compare(a, b) in Expression

c# expression expression-trees

Question

I've been teaching myself Expression Trees since yesterday and I'm having problems comparing two string values. I've made this test case that fails with the error:

No method 'Compare' on type 'System.String' is compatible with the supplied arguments.

Fails at run-time on the left = Expression.Call(

Type type = typeof(string);
Expression left, right;
left = Expression.Constant("1", type);
right = Expression.Constant("2", type);
// fails at run-time on the next statement
left = Expression.Call(
    typeof(string),
    "Compare",
    new Type[] { type, type },
    new Expression[] { left, right });
right = Expression.Constant(0, typeof(int));

I will use the resulting left & right in a Expression.Equal, LessThan, LessThanOrEqual, GreaterThan or GreaterThanOrEqual. That is the reason for the Compare method.

I'm sure its something simple, and I've boiled down my code to this simple test case. Anyone see where I've gone wrong?

Accepted Answer

This is the problem in your Expression.Call code:

new Type[] { type, type },

That's trying to call string.Compare<string, string> - they're the generic arguments, not the types of the normal parameters. Given that it's a non-generic method, just use null here.

Short but complete program:

using System;
using System.Linq.Expressions;

class Test
{
    static void Main()
    {
        var left = Expression.Constant("1", typeof(string));
        var right = Expression.Constant("2", typeof(string));
        var compare = Expression.Call(typeof(string),
                                      "Compare",
                                      null,
                                      new[] { left, right });
        var compiled = Expression.Lambda<Func<int>>(compare).Compile();
        Console.WriteLine(compiled());
    }
}

Popular Answer

I was trying to do similar as a lambda where clause (LINQ to SQL), and since various searches landed me on this page, so I'll share such a solution here in case it helps others who land here.

It is easiest when you simplify what you are doing with a generic expression of the Compare.

    public static Expression CompareLessThanOrEqualTo(Expression e1, Expression e2)
    {
        var compare = Expression.Call(typeof(string),
                           "Compare",
                           null,
                           new[] { e1, e2 });

        return Expression.LessThanOrEqual(compare, Expression.Constant(0));
    }

Then you use can this expression just like any other

    public static Expression<Func<TypeOfParent, bool>> PropertyLessThanOrEqualString<TypeOfParent, String>(PropertyInfo property, String value)
    {
        var parent = Expression.Parameter(typeof(TypeOfParent));
        var expressionBody = CompareLessThanOrEqualTo(Expression.Property(parent, property), Expression.Constant(value));
        return Expression.Lambda<Func<TypeOfParent, bool>>(expressionBody, parent);
    }

Witch can be used such as

public static IQueryable<T> ApplyFilters<T>(this IQueryable<T> query, List<GridFilters> gridFilters)
{
    // foreach filter
        // Get property (propertyInfo)
        // Get Value(s)
        // Apply Filter
        query = query.Where(PropertyLessThanOrEqualString<T, string>(propertyInfo, value1));

    // ...

    return query;
}

This is useful if you have a list of filters chosen by the user that you need to apply, where the values and operators can be both chosen. (Starts With, Contains, Between Range)



Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Is this KB legal? Yes, learn why