I have a simple lambda expression that I would like to compile and invoke
Expression< Func< Commands, bool>> expression = c => c.IsValid("test");
but when I do the following:
LambdaExpression le = Expression.Lambda(expression.Body);
object result = le.Compile().DynamicInvoke();
the compile throws the error:
variable 'c' of type 'ConsoleApplication1.Commands' referenced from scope '', but it is not defined
How do you set the instance variable for this expression?
Why not just compile the expression itself? If you'd like to invoke it with some specific 'ConsoleApplication1.Commands' instance multiple times you could then just close over that instance:
var validator = expression.Compile();
var c = new Commands();
var validatorForC = () => validator(c);
Otherwise you'll need to build call expression, like this:
var c = new Commands();
var le = Expression.Lambda(Expression.Invoke(expression, Expression.Constant(c)));
object result = le.Compile().DynamicInvoke();
or you can make ExpressionVisitor which will replace all occurences of the 'c' parameter in 'expression.Body' with Expression.Constant.
Related
i need to dinamically generate expression like this:
Expression<Func<MyClass, bool>> expr = x => (x.SomeField.CompareTo(someValue) <= 0);
trying to do it like this:
var paramExpr = Expression.Parameter(typeof(MyClass), "x");
Expression<Func<MyClass, FieldType>> pathToField = x => x.SomeField;
Expression path = pathToField;
if (!(path is LambdaExpression lambdaMember))
throw ...;
Expression valueExpr = Expression.Constant(someValue);
var bodyExpr = Expression.LessThanOrEqual(Expression.Call(lambdaMember.Body, "CompareTo", null, valueExpr ), Expression.Constant(0));
return Expression.Lambda<Func<MyClass, FieldType>>(bodyExpr, paramExpr);
but always getting error when trying to compile this:
variable 'x' of type 'MyClass' referenced from scope '', but it is not defined
how i could do this correctly?
The problem here is that you're using lambdaMember.Body, which references the x from x => x.SomeField - but because you only used the .Body, that is undefined - and is unrelated to the x from Expression.Parameter(typeof(MyClass), "x");
In the general case, there are 2 options here:
invoke the entire lambda (i.e. lambdaMember, not lambdaMember.Body) - passing in the arguments to use for the parameters
rewrite the inner lambda at runtime using ExpressionVisitor - swapping out instances of the x from the inner expression with whatever you wanted to use as the argument - presumably paramExpr
The first option is easier, and is just Expression.Invoke:
var bodyExpr = Expression.LessThanOrEqual(
Expression.Call(Expression.Invoke(lambdaMember, paramExpr),
"CompareTo", null, valueExpr), Expression.Constant(0));
Note: there is a third option in this case, since it is a relatively simple example - you can just hijack the parameter from the inner expression and use it instead of declaring paramExpr as a new parameter expression:
var paramExpr = lambdaMember.Parameters.Single();
Expression valueExpr = Expression.Constant(someValue);
var bodyExpr = Expression.LessThanOrEqual(
Expression.Call(lambdaMember.Body,
"CompareTo", null, valueExpr), Expression.Constant(0));
return Expression.Lambda<Func<MyClass, FieldType>>(bodyExpr, lambdaMember.Parameters);
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.
I am trying to get more familiar with expression trees.
I have created a simple switch expression as follows:
var paramExp = Expression.Parameter(typeof(int));
Expression<Func<int>> defaultBodyExp = () => 4;
var switchBodyExp = Expression.Switch(paramExp, defaultBodyExp,
Expression.SwitchCase(defaultBodyExp, Expression.Constant(1)));
var func = Expression.Lambda<Func<int, int>>(switchBodyExp, paramExp).Compile();
int result = func(6);
But this raises the following exception when trying to call Compile:
Expression of type 'System.Func`1[System.Int32]' cannot be used for return type 'System.Int32'
I can get it working by doing the following:
var paramExp = Expression.Parameter(typeof(int));
//Expression<Func<int>> defaultBodyExp = () => 4;
var defaultBodyMethodInfo = typeof(Program).GetMethod("DefaultBody");
var switchBodyExp = Expression.Switch(paramExp, Expression.Call(defaultBodyMethodInfo),
Expression.SwitchCase(Expression.Call(defaultBodyMethodInfo), Expression.Constant(1)));
var func = Expression.Lambda<Func<int, int>>(switchBodyExp, paramExp).Compile();
int result = func(6);
public static int DefaultBody()
{
return 4;
}
But I'm really not sure why my first example doesn't work and I'd love to know how to do that properly so I don't have to declare a method like in my second example. Do I need to be doing some sort of call invoke like in the second example?
I've had a look at some of the other similar answers on SO, but I'm new enough with Expression Trees this that I'm really not sure if they answer my problem or not.
The lambda expression Expression<Func<int>> can only be used directly in a places that expect Func<int> (like Enumerable.Select) or Expression<Func<int>> (like Queryable.Select, but you need to wrap it inside Expression.Quote).
In order to use it in a places which expect int like in your case, it needs to be converted to an expression that represents the result of the "execution" of the lambda expression. Let change the original code to
Expression<Func<int>> defaultBodyLambda = () => 4;
var defaultBodyExp = ???;
and the question is what should ??? be.
One possible (and the general) solution is to use the Expression.Invoke method:
var defaultBodyExp = Expression.Invoke(defaultBodyLambda);
Or, since this particular lambda expression has no parameters, we can simply use the LambdaExpression.Body property:
var defaultBodyExp = defaultBodyLambda.Body;
I'm working with expressions and I have the following expression
//p=> 5 == p % 5
var p = Expression.Parameter(typeof(int), "p");
var e = Expression.MakeBinary(ExpressionType.Equal,
Expression.Constant(5),
Expression.Modulo(
p,
Expression.Constant(5)
));
var lambda = Expression.Lambda(e, Expression.Parameter(typeof(int), "p"));
var func = lambda.Compile();
I'm getting the current exception:
An unexpected exception type was thrown
Expected: System.NotImplementedException
but was: System.InvalidOperationException : variable 'p' of type 'System.Int32' referenced from scope '', but it is not defined
Now this makes sense but what I find confusing is that when the Lambda expression is compiled does the Expression "compiler" keep reference of all variables in it's stack not the names?
Which means it could be possible to build a lambda expression
(p,p)=>p % p == 5
where each p is different?
How is this information stored at runtime?
Yes, two parameters created with the same type and name are different.
Change the line
var lambda = Expression.Lambda(e, Expression.Parameter(typeof(int), "p"));
to
var lambda = Expression.Lambda(e, p);
I'm using System.Linq.Expressions
I was attempting to build a simple LambdaExpression that includes a MemberExpression. If I create the MemberExpression explicitly with the System.Linq.Expressions API (e.g. MakeMemberAccess), I will get the error "InvalidOperationExpression variable 'x' referenced from scope '', but it is not defined" when I call Compile() on the LambdaExpression.
For example ,this is my code
Expression<Func<Customer, string>> expression1, expression2, expression3;
Func<Customer, string> fn;
expression1 = (x) => x.Title;
fn = expression1.Compile();//works
fn(c);
MemberExpression m;
m = Expression.MakeMemberAccess(
Expression.Parameter(typeof(Customer), "x"), typeof(Customer).GetProperty("Title"));
expression2 = Expression.Lambda<Func<Customer, string>>(m,
Expression.Parameter(typeof(Customer), "x"));
m = Expression.Property(Expression.Parameter(typeof(Customer),"x"), "Title");
expression3 = Expression.Lambda<Func<Customer, string>>(m,
Expression.Parameter(typeof(Customer), "x"));
fn = expression3.Compile();//InvalidOperationExpression variable 'x' referenced from scope '', but it is not defined
fn = expression2.Compile();//InvalidOperationExpression variable 'x' referenced from scope '', but it is not defined
expression2 and expression3 throw an exception when the Compile() method is called, but expression1 does not; expression1 works. Why is this? How do I create an MemberExpression like in expressions 2, 3 and get them to work (not throw an exception) when I call Compile()?
Thanks
You're creating different parameters called "x" several times. If you use a single ParameterExpression, it should all work fine.
ParameterExpression p = Expression.Parameter(typeof(Customer), "x");
MemberExpression m = Expression.MakeMemberAccess(p,
typeof(Customer).GetProperty("Title"));
expression2 = Expression.Lambda<Func<Customer, string>>(m, p);
m = Expression.Property(p, "Title");
expression3 = Expression.Lambda<Func<Customer, string>>(m, p);
fn = expression3.Compile();
fn = expression2.Compile();
Basically parameter expressions aren't matched by name - you've got to use the same one everywhere. It's a bit of a pain, but there we go...