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