Scoping in Expressions c# - c#

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);

Related

Complex Expression Tree with no changing context of param

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);

Stuck on simple single param lambda with bool return value

I'm stuck on a lambda with a single int parameter and a bool return value:
Expression<Func<int, bool>> myFunc = x => x == 5;
First, I tried this that returns a new Func that I can't make sense of; I was expecting a true boolean value:
var boolResult = Expression.Lambda(myFunc).Compile().DynamicInvoke(5);
Then I tried to explictly set the function parameters and return type instead:
var param = Expression.Parameter(typeof(int), "x");
var fn = Expression.Lambda<Func<int, bool>> (myFunc, param).Compile();
, but this throws an error:
System.ArgumentException : Expression of type
'System.Func`2[System.Int32,System.Boolean]' cannot be used for return
type 'System.Boolean'
Which is weird, but I tried to convert the expression:
var fn = Expression.Lambda<Func<int, bool>> (
Expression.Convert(myFunc,
typeof(Func<int, bool>))
, param).Compile();
var boolResult = fn.Invoke(5);
, this however did not help and gives the same error:
System.ArgumentException : Expression of type
'System.Func`2[System.Int32,System.Boolean]' cannot be used for return
type 'System.Boolean'
Any idea of what I'm doing wrong here?
The error in your dynamic invocation code is the way in which you construct your lambda. When you pass myFunc as expression, you get a lambda that returns Func<int,bool>.
If you want to make a new LambdaExpression that takes int and return bool, you could harvest Body and Parameters of your myFunc object, like this:
var b = Expression.Lambda(myFunc.Body, myFunc.Parameters).Compile().DynamicInvoke(5);
or, since myFunc is already a LambdaExpression, you could compile it directly:
var c = myFunc.Compile().DynamicInvoke(6);
Demo.

How do I create a dynamic Labda Expression using an outer variable?

I have a scenario where I have two tables 'side' and 'item'.
I'm trying to create the following query:
_db.SideTable.Where(s => s.Active && !_db.ItemTable.Any(i => i.SIDE_1 == s.ID));
But where SIDE_1 is actually dynamic (SIDE_1 through SIDE_20) where the number 1-20 comes from a variable 'sideId'.
I've tried creating doing the following:
// Access the parameter's SIDE_xxx property
ParameterExpression param = Expression.Parameter(typeof(ItemTable));
Expression property = Expression.Property(param, "SIDE_" + sideId);
// Access the outer variable's ID property
ParameterExpression var = Expression.Variable(typeof(SideTable), "s");
Expression property2 = Expression.Property(var, "ID");
// Make the ID property value int? since the SIDE_xxx property may be null and otherwise the expression will fail
var valExpr = Expression.Convert(property2, typeof(int?));
// The body of the expression is simply an equal check
Expression body = Expression.Equal(property, valExpr);
// Create the expression
var expression = Expression.Lambda<Func<Artikel, bool>>(body, param);
// Get the data
_db.SideTable.Where(s => s.Active && !_db.ItemTable.Any(expression)).ToList();
When I run this however I'm greeted with the following exception:
The parameter 's' was not bound in the specified LINQ to Entities query expression
I guess I'm not using the right syntax to access the outer 's' property correctly, but I have no idea how to do it otherwise.

LambdaExpression Variable Referenced From Scope But Not Defined

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.

MemberExpression: InvalidOperationExpression variable 'x' referenced from scope '', but it is not defined

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...

Categories

Resources