74

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.

Schotime
  • 15,707
  • 10
  • 46
  • 75

9 Answers9

134

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.

Bryan Watts
  • 44,911
  • 16
  • 83
  • 88
  • 1
    Get this error Expression of type 'System.Double' cannot be used for return type 'System.Object' when it resolves to a double as in the example i used. – Schotime Apr 11 '10 at 13:12
  • 5
    Had to add: var expression = Expression.Convert(member, typeof(object)); in the first line of the function to fix the above error with double conversion! – Schotime Apr 11 '10 at 14:23
  • Ah yes, I sometimes forget you have to be explicit with expression trees where C# is implicit (like conversions). I'm glad this works for you. – Bryan Watts Apr 11 '10 at 18:18
  • **Note:** If you have a **parameterless expression** `expr` of type `Expression>` (for example `expr` is containing the expression `() => myObj`), then the one-liner `object exprValue = expr.Compile()();` does the trick. Cast it afterwards to the type you require. Useful in some scenarios. – Matt Mar 19 '20 at 08:37
41
 MemberExpression right = (MemberExpression)((BinaryExpression)p.Body).Right;
 Expression.Lambda(right).Compile().DynamicInvoke();
ysrb
  • 6,693
  • 2
  • 29
  • 30
Glennular
  • 17,827
  • 9
  • 58
  • 77
  • Fastest, most concise way of getting the result. – iggymoran Oct 09 '12 at 15:41
  • 1
    Cant believe something that involves `DynamicInvoke` can be *fastest* @iggymoran did you test it? Or you meant quickest to type? ;) – nawfal Jun 04 '13 at 16:52
  • Quickest to type and easiest to understand exactly what's going on. DynamicInvoke ends up using reflection to execute it and isn't the fastest thing in the world. Bryan Watts' answer gets around this issue by getting a func and executing that (just with an invoke). When I first came around this answer, it was just easier to understand what was going on. – iggymoran Jun 06 '13 at 08:38
  • I would've given you +10 if I could :) fantastic – icesar Oct 26 '13 at 07:58
29

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.

Marc Gravell
  • 1,026,079
  • 266
  • 2,566
  • 2,900
  • If you had another level of nesting in the Object would anything change? eg. p.Product.Price – Schotime Apr 11 '10 at 12:58
  • 3
    @Schotime - indeed it would. To handle this in a generic way, see `Evaluate` and `TryEvaluate` here: http://code.google.com/p/protobuf-net/source/browse/trunk/protobuf-net.Extensions/ServiceModel/Client/ProtoClientExtensions.cs – Marc Gravell Apr 11 '10 at 15:06
  • 1
    @MarcGravell Which is faster: Compiling a `MemberExpression` then evaluating it, or reaching its `PropertyInfo/FieldInfo` then evaluating it as in `TryEvaluate`? – Ashraf Sabry Jan 08 '15 at 10:06
  • 2
    @AshrafSabry that depends how many times you are doing it, and whether you are reusing the delegate – Marc Gravell Jan 08 '15 at 14:02
5

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"

t_warsop
  • 1,170
  • 2
  • 24
  • 38
5

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;
    }
}
Alexandre Daubricourt
  • 3,323
  • 1
  • 34
  • 33
2

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!

1

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
Kirk Broadhurst
  • 27,836
  • 16
  • 104
  • 169
1

Can you use the following:

var price = p.Price;
var q = repo.Products().Where(x=>x.Price == price).ToList()
H H
  • 263,252
  • 30
  • 330
  • 514
  • This will work, however it would be great if this didn't need to happen. Does Linq-2-Sql support the syntax I'm trying to achieve? – Schotime Apr 11 '10 at 12:12
0

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;
Paulo Santos
  • 11,285
  • 4
  • 39
  • 65