Expression Tree with string assignment and getting value - c#

I have built my own SQL Query builder that breaks apart an Expression, however, I'm having an issue trying to get the value of string defined in the same function as the lambda expression.
Here is what I am trying to do in console app:
private static void MyBuilderTest()
{
var sqlBuilder = new SqlBuilder();
// Doesn't work -- NEED GUIDANCE HERE
var testValue = "Test"; // Defined in the same function as the lambda below
sqlBuilder.Select<FooObject>(o => o.FooValue == testValue);
// Works
var someObject = new SomeObject { SomeValue = "classTest };
sqlBuilder.Select<FooObject>(o => o.FooValue == someObject.SomeValue);
}
In my builder it subclasses from ExpressionVisitor, and I override the VisitMember. I found that a string defined in at the base Console level will come back as:
Node.Expression.NodeType == ExpressionType.Constant
The Node.Expression passes back properties of:
CanReduce = false
DebugView = ".Constant<ConsoleApplication1.Program+<>c__DisplayClass1>(ConsoleApplication1.Program+<>c__DisplayClass1)"
NodeType = Constant
Type = System.Type {System.RunetimeType}
Value = {ConsoleApplication1.Program}
The Node.Expression.Value contains:
testValue = "Test" (Type: string)
How do I get this value? I've tried several things, like:
var memberType = node.Expression.Type.DeclaringType;
This passes back a ConsoleApplication1.Program type.
However, when I do:
memberType.GetProperty("testValue"); // Declaring Type from Expression
It passes back null.
The above methods work fine if I place the lambda "strings" in a class, but doesn't work if they string is defined in the console function.
Can anyone tell me how to get the string value if it's defined at the function level of the lambda?
EDITED: Added VisitMember
protected override Expression VisitMember(MemberExpression node)
{
if (node.NodeType == ExpressionType.Constant)
{
// Node.Expression is a ConstantExpression type.
// node.Expression contains properties above
// And Has Value of: {ConsoleApplication1.Program}
// Expanding Value in Watch window shows: testValue = "Test"
// How do I get this value, if the ConsoleApplication1.Program type doesn't
// even know about it? Looks like maybe a dynamic property?
}
}
EDITED
Added code to the console app example to show what works and what doesn't.

The lambda in your example has "closed over" the testValue variable, meaning the compiler has captured it as a field of the same name in an automatically generated class called ConsoleApplication1.Program+<>c__DisplayClass1>. You can use normal reflection to get the current value of that field by casting the right hand-side of the binary expression into a MemberExpression.
var testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var rhs = (MemberExpression) ((BinaryExpression) expr.Body).Right;
var obj = ((ConstantExpression) rhs.Expression).Value;
var field = (FieldInfo) rhs.Member;
var value = field.GetValue(obj);
Debug.Assert(Equals(value, "hello"));
testValue = "changed";
value = field.GetValue(obj);
Debug.Assert(Equals(value, "changed"));
Alternatively you can change your variable into a constant.
const string testValue = "hello";
var expr = (Expression<Func<string, bool>>) (x => x == testValue);
var value = ((ConstantExpression) ((BinaryExpression) expr.Body).Right).Value;
Debug.Assert(Equals(value, "hello"));

Instead of doing this by yourself, have a look at PartialEvaluator from Matt Warren. It replaces all references to constants with the constants themselves.

Related

How to deconstruct expression tree to get value of captured variable

Consider the following block of code:
Expression<Func<string, bool>> pred;
{
string s = "test";
pred = x => x == s;
}
// how to retrieve the value of s from just pred?
The only variable in scope after this block is pred. How can we retrieve the value of the captured variable s, just using pred? I can see the value in the debugger by expanding the object graph of pred, but how do I do that in code?
You have to do quite a bit of casting to get to the underlying value of ConstantExpression since the compiler will tuck the value away.
The following gets the right node via BinaryExpression, checks if the right node is a ConstantExpression, and uses the FieldInfo of the right node to get the value:
var rightNodeMemberExpression = ((pred.Body as BinaryExpression).Right)
as MemberExpression;
var fieldInfo = rightNodeMemberExpression.Member as FieldInfo;
if (rightNodeMemberExpression.Expression is ConstantExpression exp)
{
var val = exp.Value;
var retrievedValue = fieldInfo.GetValue(val);
Console.WriteLine(retrievedValue); // will output "test"
}

Change Parameter Value of Expression<Func<<string>>

Let's say I've got the class Item which looks like this
public class Item
{
// ..
Expression<Func<string>> Callback {get; set;}
}
Item defines a property called Callback which can be used like this
public string TestFunction(string ident, DateTime value1, DateTime value2)
{
return string.Join(";", ident, value1, value2);
}
// ..
Item x = new Item();
x.Callback = () => TestFunction("Hello there", DateTime.Now.Date, DateTime.Now);
Console.WriteLine(x.Callback.Compile().Invoke()); // prints the expected output
That works just well. Now, what I'm trying to do is change the value of the DateTime parameters.
I've already figured out how to get the arguments:
MethodCallExpression body = (MethodCallExpression)x.Callback.Body;
foreach(ConstantExpression arg in body.Arguments) {
if(arg.Type == typeof(DateTime)) {
//arg.Value = => READONLY!
}
}
However, I can't assign a new value to arg.Value because doesn't have a setter.
There seems to be something called ExpressionVisitor but I'm unsure if that's something I need.
Is there any way to achieve what I'm trying to do?
Thank you in advance
__
Update
I almost got it working with #Guru Stron help but there's still a small problem.
This piece of code works perfectly fine:
var newParams = new[] { Expression.Constant("testIdent"), Expression.Constant(DateTime.Now), Expression.Constant(DateTime.Now) };
However, the following code throws an
Expression of type 'System.Linq.Expressions.ConstantExpression' cannot be used for parameter of type 'System.String' of method 'System.String TestFunction(System.String, System.DateTime, System.DateTime)'
Exception.
List<ConstantExpression> para = new List<ConstantExpression>();
foreach (var arg in body.Arguments) {
if (arg.Type == typeof(DateTime)) {
para.Add(Expression.Constant(DateTime.Now));
continue;
}
para.Add(Expression.Constant(arg));
}
var exprBody = Expression.Call(body.Object, body.Method, para); // Exception is thrown here
The error is pretty obvious but I can't seem to find a way to convert the parameter to the correct type.
The reason why I changed the code is because I don't know the amount of parameters, so I tried to loop through them any only change the ones I need since the order remains correct.
Any ideas?
You will need to build a new expression and pass new desired parameters to it:
MethodCallExpression body = (MethodCallExpression)x.Callback.Body;
var newParams = new[] { Expression.Constant("NEW "), Expression.Constant(DateTime.Now), Expression.Constant(DateTime.Now)};
var exprBody = Expression.Call(body.Object, body.Method, newParams );
var newExpr = Expression.Lambda<Func<string>>(exprBody);
var newFunc = newExpr.Compile();
Console.WriteLine(newFunc()); // "NEW ;03-Jun-20 5:07:16 PM;03-Jun-20 5:07:16 PM"

Return value of property based on property name

How do I build expression tree in C# that returns value of a property based on the name of the property
Func<Foo, long> getValue(string propertyName)
{
// i think that the beginning of the expression tree would look like this
// but i'm not sure this is correct
var inputParameter = Expression.Parameter(typeof(Foo));
var desiredProperty = typeof(Foo).GetProperty(propertyName);
var valueOfProperty = Expression.Property(inputParameter, desiredProperty);
// ... ??? todo: expression that returns value
}
Call to this function looks like this which is part of another expression that is passed to Linq's Select method:
value = getValue("Bar").Invoke(FooInstance)
Should be enough:
var lambda = Expression.Lambda<Func<Foo, long>>(valueOfProperty, inputParameter);
return lambda.Compile();
Anyway - what's the purpose for building Expression when you could get value directly via reflection?
return someFoo => (long)desiredProperty.GetValue(someFoo);

Expression Tree ToString() method generates strange string WHY?

I need string conversion of an Expression Tree so
I create an Expression Tree and use ToString method like this
var exp = ((Expression<Func<UserDetailInfo, bool>>) (x => x.OperationID == operationId)).ToString();
but result is strange
x => (x.OperationID == value(TCS.Proxy.PermissionProxy+<>c__DisplayClass5).operationId)
TCS.Proxy.PermissionProxy is my class in WCF proxy project !!! (I send expression from client to proxy)
but when I create this Expression myself everything is good
var entity = Expression.Parameter(typeof(UserDetailInfo));
var constant = Expression.Constant(operationId);
var e = Expression.Equal(Expression.Property(entity, "OperationID"), constant);
var exp = Expression.Lambda<Func<UserDetailInfo, bool>>(e, entity).ToString();
and result is ok
Param_0 => (Param_0.OperationID == operationId) // I Need this
How I can use ToString() can generates result like above ?
Why two results is different ?
* I Convert Expression to String for transfer it from client to WCF service so I need correct string for convert in server side from string to Expression
This is because your right hand side member is not a constant, it is a captured variable. The TCS.Proxy.PermissionProxy+<>c__DisplayClass5 part means in the class TCS.Proxy.PermissionProxy it had to create 5 new classes that holds values that where passed in via variable capture and this specific lambda uses the 5th one it created.
Your code (You never show your function so I made some guesses)
namespace TCS.Proxy
{
class PermissionProxy
{
public void SomeFunction()
{
int operationId = 0;
var exp = ((Expression<Func<UserDetailInfo, bool>>) (x => x.OperationID == operationId)).ToString()
}
}
}
Is getting re-written to something similar (It's actually a lot different but this example gets the point across) to
namespace TCS.Proxy
{
public class PermissionProxy
{
private class c__DisplayClass5
{
public int operationId;
}
public void SomeFunction()
{
int operationId = 0;
var <>c__DisplayClass5 = new c__DisplayClass5();
<>c__DisplayClass5.operationId = operationId;
var exp = ((Expression<Func<UserDetailInfo, bool>>) (x => x.OperationID == <>c__DisplayClass5.operationId)).ToString()
}
}
}
Which is different than what you manually created. If you want to "unbox" these custom classes you will need to write up a ExpressionVisitor that will go through the expression and re-write it in to the form you want to go over the wire.

Does an expression tree Parameter need to reuse same instance?

I'm using a combination of reflection and expression trees, and want to pass back certain property accessors from a class to a calling method. My current code has a method traversing the class and returning a list of MemberExpressions. The caller then iterates over the member expressions and creates lambdas, which should then be called with an instance of the inspected class to return the value of the property.
Here is a sample of what it would look like without the method calls (Runnable in LINQPad):
void Main()
{
var t = new Test { Prop = "Test" };
var property = t.GetType().GetProperty("Prop");
var baseType = Expression.Parameter(typeof(Test), "baseType");
var memberAccess = Expression.MakeMemberAccess(baseType, property);
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, Expression.Parameter(typeof(Test), "baseType"));
var func = lambda.Compile();
var result = func(t);
result.Dump();
}
class Test {
public string Prop { get; set; }
}
This does not work, throwing this exception:
InvalidOperationException: variable 'baseType' of type 'UserQuery+Test' referenced from scope '', but it is not defined
However, if I change the creation of the lambda to this:
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, baseType);
That is, replace the Expression.Parameter with the variable used earlier, then it works. This is not (easily) possible in the scenario where I want to use it, since I would have to return the original parameter along with the list (I could return a tuple, of course, but I would prefer not to, if it is not necessary).
Why does it work like this? Inspecting the DebugView of the lambda, they are exactly the same no matter what approach is used:
.Lambda #Lambda1<System.Func`2[UserQuery+Test,System.String]>(UserQuery+Test $baseType)
{
$baseType.S
}
Yes, you need to refer ParameterExpression, used earlier. This won't compile too:
private String Foo(Test myParam)
{
return myAnotherParam.MyProperty;
}
With instatiating new ParameterExpression in lambda, you're doing the same thing (but note, when making lambda, you're doing it in reversed order - first, you're constructing a method body, then - a method declaration):
// return myAnotherParam.MyProperty;
var baseType = Expression.Parameter(typeof(Test), "baseType");
var memberAccess = Expression.MakeMemberAccess(baseType, property);
// private String Foo(MyClass myParam)
var lambda = Expression.Lambda<Func<Test, string>>(memberAccess, Expression.Parameter(typeof(Test), "baseType"));

Categories