Access the value of a member expression

C#LinqLambdaExpression Trees

C# Problem Overview


If i have a product.

var p = new Product { Price = 30 };

and i have the following linq query.

var q = repo.Products().Where(x=>x.Price == p.Price).ToList()

In an IQueryable provider, I get a MemberExpression back for the p.Price which contains a Constant Expression, however I can't seem to get the value "30" back from it.

Update I have tried this but it doesn't seem to work.

var memberExpression = (MemberExpression)GetRootConstantExpression(m);
var fi = (PropertyInfo)memberExpression.Member;
var val = fi.GetValue(((ConstantExpression)memberExpression.Expression).Value, null);

Cheers.

C# Solutions


Solution 1 - C#

You can compile and invoke a lambda expression whose body is the member access:

private object GetValue(MemberExpression member)
{
    var objectMember = Expression.Convert(member, typeof(object));

    var getterLambda = Expression.Lambda<Func<object>>(objectMember);

    var getter = getterLambda.Compile();

    return getter();
}

Local evaluation is a common technique when parsing expression trees. LINQ to SQL does this exact thing in quite a few places.

Solution 2 - C#

 MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right;
 Expression.Lambda(right).Compile().DynamicInvoke();

Solution 3 - C#

The constant expression is going to point to a capture-class generated by the compiler. I've not included the decision points etc, but here's how to get 30 from that:

var p = new Product { Price = 30 };
Expression<Func<Product, bool>> predicate = x => x.Price == p.Price;
BinaryExpression eq = (BinaryExpression)predicate.Body;
MemberExpression productToPrice = (MemberExpression)eq.Right;
MemberExpression captureToProduct = (MemberExpression)productToPrice.Expression;
ConstantExpression captureConst = (ConstantExpression)captureToProduct.Expression;
object product = ((FieldInfo)captureToProduct.Member).GetValue(captureConst.Value);
object price = ((PropertyInfo)productToPrice.Member).GetValue(product, null);

price is now 30. Note that I'm assuming that Price is a property, but in reality you would write a GetValue method that handles property / field.

Solution 4 - C#

If you had a class:

public class Item
{
    public int Id { get; set; }
}

and an instance of the object:

var myItem = new Item { Id = 7 };

You can get the value of Id using an Expression using the following code:

Expression<Func<Item, int>> exp = x => x.Id;
var me = exp.Body as MemberExpression;
var propInfo = me.Member as PropertyInfo;
var myValue = propInfo.GetValue(myItem, null);

myValue will contain "7"

Solution 5 - C#

Using Expression.Lambda(myParameterlessExpression).Compile().Invoke() has several drawbacks:

  • .Compile() is slow. It can take multiple milliseconds to complete even for small expression fragments. The Invoke-call is super-fast afterwards though, takes only few nanoseconds for simple arithmetic expressions or member accesses.
  • .Compile() will generate (emit) MSIL code. That might sound perfect (and explains the excellent execution speed) but the problem is: That code takes up memory, which can not be freed before the application finishes, even when the GC collected the delegate-reference!

One can either avoid Compile() altogether to avoid these issues or cache the compiled delegates for re-using them. This little library of mine offers both interpretation of Expressions as well as cached compilation, where all constants and closures of the expression get replaced by additional parameters automatically, which are then re-inserted in a closure, which is returned to the user. Both processes are well-tested, used in production, both have their pros and cons against each other but are well over 100x faster than Compile() - and avoid the memory leak!

Solution 6 - C#

As of 2020

This helper method will gracefully retrieve any expression value, without "compiling hack" :

public static object GetMemberExpressionValue (MemberExpression expression)
{
    // Dependency chain of a MemberExpression is of the form:
    // MemberExpression expression
    //    MemberExpression expression.Expression
    //        ... MemberExpression expression.[...].Expression
    //            ConstantExpression expression.[...].Expression.Expression <- base object
    var dependencyChain = new List<MemberExpression>();
    var pointingExpression = expression;
    while (pointingExpression != null)
    {
        dependencyChain.Add(pointingExpression);
        pointingExpression = pointingExpression.Expression as MemberExpression;
    }

    if (!(dependencyChain.Last().Expression is ConstantExpression baseExpression))
    {
        throw new Exception(
            $"Last expression {dependencyChain.Last().Expression} of dependency chain of {expression} is not a constant." +
            "Thus the expression value cannot be found.");
    }

    var resolvedValue = baseExpression.Value;

    for (var i = dependencyChain.Count; i > 0; i--)
    {
        var expr = dependencyChain[i - 1];
        resolvedValue = new PropOrField(expr.Member).GetValue(resolvedValue);
    }

    return resolvedValue;
}

PropOrField class :

public class PropOrField
{
    public readonly MemberInfo MemberInfo;

    public PropOrField (MemberInfo memberInfo)
    {
        if (!(memberInfo is PropertyInfo) && !(memberInfo is FieldInfo))
        {
            throw new Exception(
                $"{nameof(memberInfo)} must either be {nameof(PropertyInfo)} or {nameof(FieldInfo)}");
        }

        MemberInfo = memberInfo;
    }

    public object GetValue (object source)
    {
        if (MemberInfo is PropertyInfo propertyInfo) return propertyInfo.GetValue(source);
        if (MemberInfo is FieldInfo fieldInfo) return fieldInfo.GetValue(source);

        return null;
    }

    public void SetValue (object target, object source)
    {
        if (MemberInfo is PropertyInfo propertyInfo) propertyInfo.SetValue(target, source);
        if (MemberInfo is FieldInfo fieldInfo) fieldInfo.SetValue(target, source);
    }

    public Type GetMemberType ()
    {
        if (MemberInfo is PropertyInfo propertyInfo) return propertyInfo.PropertyType;
        if (MemberInfo is FieldInfo fieldInfo) return fieldInfo.FieldType;

        return null;
    }
}

Solution 7 - C#

q is of type List<Product>. The List doesn't have a Price property - only the individual Products.

The first or last Product will have a price.

q.First().Price
q.Last().Price

If you know there's only one in the collection you can also flatten it using Single

q.Single().Price

Solution 8 - C#

Can you use the following:

var price = p.Price;
var q = repo.Products().Where(x=>x.Price == price).ToList()

Solution 9 - C#

And what exactly are you trying to accomplish?

Because to access the value of Price, you'd have to do something like:

var valueOfPrice = q[0].Price;

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionSchotimeView Question on Stackoverflow
Solution 1 - C#Bryan WattsView Answer on Stackoverflow
Solution 2 - C#GlennularView Answer on Stackoverflow
Solution 3 - C#Marc GravellView Answer on Stackoverflow
Solution 4 - C#t_warsopView Answer on Stackoverflow
Solution 5 - C#Fabian HeimbürgerView Answer on Stackoverflow
Solution 6 - C#Alexandre DaubricourtView Answer on Stackoverflow
Solution 7 - C#Kirk BroadhurstView Answer on Stackoverflow
Solution 8 - C#Henk HoltermanView Answer on Stackoverflow
Solution 9 - C#Paulo SantosView Answer on Stackoverflow