I'm facing a problem with executing a query against a Queryable dataset. The original call looks like this:
books = books.Where(b => (GetPropertyValue(b, filter.CategoryProperties.DbName) == null ? 0 : Convert.ToInt32(GetPropertyValue(b, filter.CategoryProperties.DbName))) < Convert.ToInt32(filter.Value));
This gets me the Method not recognized error. This of course is expected due to the call to GetPropertyValue. I then read that I should build the expression tree myself. That result in the following code:
public IQueryable<Books> GetExpression(IQueryable<Books> books, BookCategoryMapping filter)
{
var booksExpression = Expression.Parameter(typeof(Books), "b");
var methodInfo = this.GetType().GetMethod("GetPropertyValue");
var value = Expression.Call(methodInfo, booksExpression, Expression.Constant(filter.CategoryProperties.DbName));
var left = Expression.Constant(value);
var right = Expression.Constant(filter.Value);
var expression = Expression.Equal(left, right);
var whereExpression = Expression.Call(typeof(Queryable), "Where", new Type[] { books.ElementType }, books.Expression, Expression.Lambda<Func<Books, bool>>(expression, new ParameterExpression[] { booksExpression }));
return books.Provider.CreateQuery<Books>(whereExpression);
}
Only problem being, that I get the same error. It seems like the following line only produces an expression and not the value of said expression.
var value = Expression.Call(methodInfo, booksExpression, Expression.Constant(filter.CategoryProperties.DbName));
Any help producing the correct expression tree would be greatly appreciated :-)
EDIT: Here's the GetPropertyValue method:
public static object GetPropertyValue(object obj, string name)
{
try
{
return obj.GetType().GetProperty(name)?.GetValue(obj, null);
}
catch (Exception ex)
{
LogManager.Log(LogLevel.Error, null, ex);
}
return obj;
}
The method below generates an Expression<Func<Book, bool>>
which determines whether a given property of a Book
is less than a given (constant) value. The error-checking code you currently have in GetPropertyValue
can probably be replaced by catching the ArgumentException
which will be thrown when you try to create an expression for a nonexistent property.
Note that I have assumed that the property you're accessing is genuinely numeric, and that your call to Convert.ToInt32
was only necessary because your GetPropertyValue
method returns an object
.
Expression<Func<Book, bool>> GenerateLessThanExpression(string propertyName, int value)
{
var parameter = Expression.Parameter(typeof (Book));
var property = Expression.Property(parameter, propertyName);
var comparison = Expression.LessThan(property, Expression.Constant(value));
return Expression.Lambda<Func<Book, bool>>(comparison, parameter);
}
Here's a sample usage to return only very short books:
var filter = GenerateLessThanExpression("Pages", 5);
var filtered = books.Where(filter);