So I don't quite understand this. I'm trying to created a MethodCallExpression, but I'm getting a runtime error stating that the method cannot be called with exactly the type it expects? I'm fairly new to Expression trees, so I'm sure I've got something backwards, but just about every example I try to adapt leads to this.
using System;
using System.Linq;
using System.Linq.Expressions;
namespace ConsoleApplication4
{
class Program
{
static void Main(string[] args)
{
var helper = new Helper();
var expr = helper.GetMatchExpr<TestClass>(t => t.Name, "foo");
}
}
public class Helper
{
public Expression<Func<T, bool>> GetMatchExpr<T>(Expression<Func<T, object>> member, string searchValue)
{
var memberExpr = member.Body as MemberExpression;
if (memberExpr == null)
throw new ArgumentException("Expected member expression");
var parameter = Expression.Parameter(typeof(T), "type");
var property = Expression.Property(parameter, memberExpr.Member.Name);
var propertyExpr = Expression.Convert(property, typeof(string));
var matchMethod = typeof(Helper).GetMethod("IsMatch", new[] { typeof(object), typeof(string) });
var valueExpr = Expression.Constant(searchValue, typeof(string));
var matchExpr = Expression.Call(property, matchMethod, propertyExpr, valueExpr); // throws exception
return Expression.Lambda<Func<T, bool>>(matchExpr, parameter);
}
public bool IsMatch(object input, string searchValue)
{
if (input == null)
return true;
var qWords = GetWords(searchValue);
var pWords = GetWords(input.ToString());
return (
from qWord in qWords
from pWord in pWords
where qWord.Equals(pWord, StringComparison.OrdinalIgnoreCase)
select qWord).Any();
}
public string[] GetWords(string input)
{
return string.IsNullOrEmpty(input)
? new string[0]
: input.Split(' ');
}
}
public class TestClass
{
public string Name { get; set; }
public string Age { get; set; }
}
}
The exception is
Additional information: Method 'Boolean IsMatch(System.Object)' declared on type 'ConsoleApplication4.Helper`1[ConsoleApplication4.TestClass]' cannot be called with instance of type 'System.Object'
As an exercise, I'm trying to build this class to where you can specify a collection of properties on object T that will be included in a search. So once all of those properties are set, you call Apply(...) and it will iterated through the collection of member expressions, grab their value, split it into individual words, do the same with the query phrase and then return any object that has any properties that match any of the words.
I've gone through probably 5 or 6 different examples that looked close to what I was trying to achieve and this is just kind of where I am at the moment.
So I guess I don't quite understand what "call IsMatch on the result of propertyExpr". Does that mean it's using the result like result.IsMatch or IsMatch(result)? If it's the latter, shouldn't anything be usable as object?
Look at your method call expression:
var matchExpr = Expression.Call(property, matchMethod, propertyExpr, valueExpr);
The first argument is the target of the method call - what you're trying to call IsMatch
on. Now IsMatch
is declared in your helper class, so you should call it on an instance of that class. For example:
var targetExpr = Expression.Constant(this, typeof(Helper));
var matchExpr = Expression.Call(targetExpr, matchMethod, propertyExpr, valueExpr);
That removes the exception - but it's still not clear that any of this code is going to help you. Having an expression tree representation of a query is typically useful when that expression tree can be converted into some other form, e.g. SQL... but no LINQ provider is going to know what your Helper.IsMatch
method does.
If your aim was to do all the matching work in a database, this solution isn't going to help you. If your aim was just to do the matching work, you could do it a lot more simply using LINQ to Objects, and just calling methods appropriately and using delegates where required, without using IQueryable<T>
at all.
See more on this question at Stackoverflow