Extract property name and nullable value from lambda expression - c#

I'm using a lambda expression and need to extract the property name and value:
public MyMapping(Expression<Func<TEntity, int?>> expression)
{
this.expression = expression;
if (this.expression != null)
{
if (expression.Body is MemberExpression)
{
this.expressionMemberName = ((MemberExpression)expression.Body).Member.Name;
}
else
{
var op = ((UnaryExpression)expression.Body).Operand;
this.expressionMemberName = ((MemberExpression)op).Member.Name;
}
}
}
The expression is setup as follows:
this.MyProperty(m => m.NumberOfDays.Value);
I want the following:
var propertyName = this.expressionMemberName;
var value = this.expression.Compile()(instance);
Because my property is nullable, I get an exception when it tries to compile the expression. Also propertyName is set to "Value" instead of NumberOfDays.
How would I go about getting this data and getting the expression to coompile?

See this post (Retrieving Property name from lambda expression) for extracting property names from Expressions. Also see this gotcha for nested/subproperties, i.e. Thing1.Thing2 from o => o.Thing1.Thing2.

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"
}

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

How do I convert the right of a lambda operator to a string?

Preface
I know it's possible to grab a variable's name using a combination of reflection and expressions. However, is it possible to grab more than just the variable's name in the case of property/field chaining?
Example Function
public void DisplayVarName<T>(Expression<Func<T>> expression){
MemberExpression memberExpression =
expression.Body as MemberExpression;
Console.WriteLine(memberExpression.Member.Name);
}
Example 1
string helloWorldStr = "Hello World";
DisplayVarName(() => helloWorldStr); // Outputs "helloWorldStr"
Example 2
Person person = new Person(){ Name = "Santa Clause" };
DisplayVarName(() => person.Name); // Outputs "Name"
Question
Is there any way to store whatever is to the right of the lambda operator in a string?
Example 2 would produce "person.Name" rather than simply "Name"
It looks like the simplest way is to iterate on the MemberExpression.Expression
public static void DisplayVarName<T>(Expression<Func<T>> expression)
{
List<string> memberNames = new List<string>();
MemberExpression memberExpression =
expression.Body as MemberExpression;
do
{
memberNames.Add(memberExpression.Member.Name);
memberExpression =
memberExpression.Expression as MemberExpression;
} while (memberExpression != null);
memberNames.Reverse();
Console.WriteLine(String.Join(".", memberNames));
}
If you just want to print out the entire body of the lambda as a string it's quite simple:
public void DisplayVarName<T>(Expression<Func<T>> expression){
Console.WriteLine(expression.Body);
}

Expression Tree with string assignment and getting value

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.

Get property value from MemberExpression

I'm attempting to retrieve the value of a property from an instance of MemberExpression.
Here is what I have so far:
protected override void VisitMember(Context context, MemberExpression node)
{
var propertyInfo = node.Member as PropertyInfo;
if(propertyInfo != null)
{
var v = propertyInfo.GetValue(node.Member , null);
val = Convert.ToString(v);
}
context.State.Append(val);
}
Depending on the approach I take there are two problems: I don't know the expected type (string, int, etc...), and/or I have not been able access the instance from the MemberExpression.
I am writing a small lambda expressions to T-SQL converter. For example (u)=> u.FirstName == u.LastName; would convert to FirstName = 'chuck'. I've almost got it working!
update
I tried the following code:
...
var propertyInfo = node.Member as PropertyInfo;
if(propertyInfo != null)
{
var o = propertyInfo.GetValue(node.Expression, null);
}
...
It did not work. I get the following error:
System.Reflection.TargetException : Object does not match target type.
update 2
This is what I am trying to accomplish:
public static Func<T, object> GetValueGetter<T>(this PropertyInfo propertyInfo)
{
if (typeof(T) != propertyInfo.DeclaringType)
{
throw new ArgumentException();
}
var instance = Expression.Parameter(propertyInfo.DeclaringType, "i");
var property = Expression.Property(instance, propertyInfo);
var convert = Expression.TypeAs(property, typeof(object));
return (Func<T, object>)Expression.Lambda(convert, instance).Compile();
}
But I do not know T at compile time.
I don't know the expected type (string, int, etc...),
Use Expression.Type
I have not been able access the instance from the MemberExpression
Use MemberExpression.Expression - obviously that's another expression, because you might have:
foo.GetBar(20).ToString().Length
in which case the Length property would be a MemberExpression, but the Expression property would give the MethodCallExpression for ToString.
I have not been able access the instance from the MemberExpression.
In your example u => u.FirstName == "chuck", there is no instance for which to fetch the FirstName property.
I think you actually want the name of the property - which is node.Member.Name (and is "FirstName" in your example). Note that this works for all MemberInfo, not just PropertyInfo, so fields will work as well. (You may want to test anyway, because Events are also member expressions, but don't make sense here.)
I don't know the expected type (string, int, etc...),
The expected type is either PropertyInfo.PropertyType or FieldInfo.FieldType.

Categories