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?
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());
}
}
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)