Convert Expression to Expression.Lambda<Func<object, bool>> - c#

I have a method that builds an expression tree, based on the Type of the object that is passed to the method. Once the tree is built, I want to convert it and return it with the return type as is shown below.
public static Expression<Func<object, bool>> BuildExpression(Type type, ...)
{
// build the expression...
ParameterExpression param = Expression.Parameter(type, "m");
Expression expression = null;
// simplified version of building the expression tree
MemberExpression member = Expression.Property(param, filter.Property);
ConstantExpression constant = Expression.Constant(filter.Value);
expression = Expression.Equal(member, constant);
// ...
// IT FAILS ON THIS LINE!!!
return Expression.Lambda<Func<object, bool>>(expression, param);
}
I've looked at a few conversion answers, but to no avail. Any advice?

Here is your code with modifications as described in my previous comment.
1) Your function returns expression that describes function with single argument. And this argument is of type Object. So you should use Object type when creating parameter "m" expression.
2) Before accessing the property parameter should be cast back to desired type. See Expression.Convert.
public static Expression<Func<object, bool>> BuildExpression(Type type, ...)
{
// build the expression...
ParameterExpression param = Expression.Parameter(typeof(Object), "m");
Expression expression = null;
UnaryExpression convert = Expression.Convert(param, type);
// simplified version of building the expression tree
MemberExpression member = Expression.Property(convert, filter.Property);
ConstantExpression constant = Expression.Constant(filter.Value);
expression = Expression.Equal(member, constant);
// ...
return Expression.Lambda<Func<object, bool>>(expression, param);
}

Related

Building an Expression that checks if two properties of the object are equal?

I'm trying to understand how Expressions work, so I imagined a method that takes an object that has two int properties and return boolean value indicate if they are equal, something like:
bool AreEqual(Foo foo)
{
return foo.Value1 == foo.Value2;
}
here's the Expression I built:
//build the parameter expression of the object
ParameterExpression parameter = Expression.Parameter(typeof(Foo), "x");
//the left member
MemberExpression leftMember = Expression.Property(parameter, "Value1");
//the right member
MemberExpression rightMember = Expression.Property(parameter, "Value2");
//the left lambda
LambdaExpression leftLmbda = Expression.Lambda(leftMember, parameter);
//the right lambda
LambdaExpression rightLambda = Expression.Lambda(rightMember, parameter);
//and here I evaluate the boolean expression:
Expression equalExpression = Expression.Equal(rightLambda, leftLmbda);
//the lambda of the equal expression
LambdaExpression lambda = Expression.Lambda(equalExpression, parameter);
//the object:
Foo foo = new Foo { Value1= 5, Value2=5 };
Delegate expression = lambda.Compile();
var eq = expression.DynamicInvoke(foo);
but it always evaluates to false.
My guess is that I only build one lambda, but don't know how to handle both properties within one lambda
Your Expression.Equal statment should be comparing the two member expressions.
//build the parameter expression of the object
ParameterExpression parameter = Expression.Parameter(typeof(Foo), "x");
//the left member
MemberExpression leftMember = Expression.Property(parameter, "Value1");
//the right member
MemberExpression rightMember = Expression.Property(parameter, "Value2");
//and here I evaluate the boolean expression:
Expression equalExpression = Expression.Equal(leftMember, rightMember);
//the lambda of the equal expression
LambdaExpression lambda = Expression.Lambda(equalExpression, parameter);
//the object:
Foo foo = new Foo { Value1 = 5, Value2 = 5 };
Delegate expression = lambda.Compile();
var eq = expression.DynamicInvoke(foo);

How do you create a lambda expression from a MemberExpression

My ultimate goal is to iterate through nested properties in a lambda expression and determine if any of the properties are null, but I am having trouble creating a new lambda expression based on a member expression.
Take this dummy method:
public static void DoStuff<TModelDetail, TValue>(Expression<Func<TModelDetail, TValue>> expr, TModelDetail detail)
{
var memberExpression = expr.Body as MemberExpression;
if (memberExpression == null && expr.Body is UnaryExpression)
{
memberExpression = ((UnaryExpression)expr.Body).Operand as MemberExpression;
}
var pe = Expression.Parameter(typeof(TModelDetail), "x");
var convert = Expression.Convert(memberExpression, typeof(object));
var wee = Expression.Lambda<Func<TModelDetail, object>>(convert, pe);
var hey = wee.Compile()(detail);
}
On the Compile.exec line I get the following error:
variable 'x' of type 'Blah' referenced from scope '', but it is not defined
where Blah is the type of TModelDetail.
How do I build the lambda with the MemberExpression? What I ultimately want to do is recursively find the root member expression, determine if it is null, and bubble up and determine if each subsequent member expression is null.
expr already contains a parameter (let's call it y) which is bound by your member expression, so expr looks like y => y.Member.Something.
When your construct the new lambda Expression wee you are giving it a new parameter x, so wee looks like x => y.Member, which doesn’t make sense.
Therefore you need to reuse the parameter from expr for wee.

"Does Not Contain" dynamic lambda expression

The code below does "Contain" expression:
private static Expression<Func<T, bool>> Contains<T>(string property, string value)
{
var obj = Expression.Parameter(typeof(T), "obj");
var objProperty = Expression.PropertyOrField(obj, property);
var contains = Expression.Call(objProperty, "Contains", null, Expression.Constant(value, typeof(string)));
var lambda = Expression.Lambda<Func<T, bool>>(contains, obj);
return lambda;
}
I am not very familiar with Expressions and I don't know how to put negation into the expression function and cannot find any suitable method in "Expression" class. Is there any similar way to create "Does Not Contain" expression dynamically?
A "does not contain" expression is exactly the same as a "does contain" expression - but wrapped with a unary negation expression. So basically you want:
// Code as before
var doesNotContain = Expression.Not(contains);
return Expression.Lambda<Func<T, bool>>(doesNotContain, obj);
Contains returns you a bool. Inverting a bool is done with the Expression.Not method.

Dynamically set out type in Expression Func

Starting with the following:
string propertyName = TextBoxPropertyToGet.Text;
ParameterExpression arg = Expression.Parameter(typeof(Item), "x");
Expression expr = Expression.Property(arg, propertyName);
LambdaExpression lambda = Expression.Lambda(expr, arg);
Expression<Func<Item, string>> expression = (Expression<Func<Item, string>>)lambda;
What I was hoping to do:
ParameterExpression arg = Expression.Parameter(typeof(Item), "x");
PropertyInfo pi = typeof(Item).GetProperty(propertyName);
Type pt = pi.PropertyType;
Expression expr = Expression.Property(arg, pi);
LambdaExpression lambda = Expression.Lambda(expr, arg);
Expression<Func<Item, pt>> expression = (Expression<Func<Item, pt>>)lambda;
but I get the error:
The type or namespace name 'pt' could not be found (are you missing a using directive or an assembly reference?) On the last line.
I don't want to use:
if(pt == typeof(string))
Expression<Func<Item, string>>....
else if(pt==typeof(decimal))
Expression<Func<Item, decimal>>....
....
....
I have also tried:
var expression = Expression.Lambda(
Expression.Convert(Expression.Property(
arg, propertyName),
pt),
arg);
AND
var lambdaMethodParamType = typeof(Func<,>).MakeGenericType(typeof(Item), pt);
var expression = typeof(Expression).GetMethods().First(x => x.Name == "Lambda" && x.IsGenericMethod).MakeGenericMethod(lambdaMethodParamType);
but then I get:
The type arguments for method 'System.Linq.Enumerable.Select<TSource,TResult>(System.Collections.Generic.IEnumerable<TSource>, System.Func<TSource,int,TResult>)' cannot be inferred from the usage. Try specifying the type arguments explicitly.
basically telling me to do what I don't want to do.
I have tried (as proposed by Servy)
string propertyName = TextBoxPropertyToGet.Text;;
ParameterExpression arg = Expression.Parameter(typeof(Item), "x");
Expression expr = Expression.Property(arg, propertyName);
LambdaExpression lambda = Expression.Lambda(expr, arg);
var x = Queryable.Select(MyContext.Items,(dynamic)lambda);
but this returns empty, and when trying to call var x = Queryable.Select(MyContext.Items,(dynamic)lambda).Distinct(); I get the error 'System.Data.Entity.Infrastructure.DbQuery<string>' does not contain a definition for 'Distinct'
You need to call Select using reflection, rather than calling it normally, if the generic arguments are not known at compile time. This means getting the Type of Queryable, getting the Select method, choosing the right overload, etc.
The alternative is to use dynamic, which does pretty much the same thing, but handles generating pretty much of that dynamic typing for you:
LambdaExpression lambda = Expression.Lambda(expr, arg);
dynamic query = Queryable.Select(data, (dynamic)lambda);

Expression of type 'System.DateTime' cannot be used for return type 'System.Object'

I've created an expression that I'm using for sorting which works fine, until I hit a DateTime field, where I get the following error (on the second line):
Expression of type 'System.DateTime' cannot be used for return type
'System.Object'
Here's my code:
ParameterExpression param = Expression.Parameter(typeof(MyEntity), "x");
Expression<Func<MyEntity, object>> sortExpression =
Expression.Lambda<Func<AMyEntity, object>>(
Expression.Property(param, sortKey), param);
Can anyone help at all?
Just add a conversion in there:
Expression<Func<MyEntity, object>> sortExpression =
Expression.Lambda<Func<AMyEntity, object>>(
Expression.Convert(
Expression.Property(param, sortKey),
typeof(object)),
param);
You appear to be expecting auto-boxing of value-types to match the return-type of the expression. Unfortunately, Expression.Lambda does not do this.
You can use Expression.Convert to perform the boxing.
var body = Expression.Convert(Expression.Property(param, sortKey), typeof(object));
var sortExpression = Expression.Lambda<Func<AMyEntity, object>>(body, param);
If for some reason you don't want the conversion operation to be present in the expression if the property is already a reference-type, you can branch as required:
Expression body = Expression.Property(param, sortKey);
if(body.Type.IsValueType)
body = Expression.Convert(body, typeof(object));

Categories

Resources