views:

377

answers:

4

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.

+1  A: 

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
ahhhh....no. repo.Products() returns an IQueryable<Product>.
Schotime
Yes but `.ToList()` on the end makes it into a list.
Kirk Broadhurst
Regardless of whether it's a List or an IQueryable, you can still use First, Last, or Single - but make no mistake, `repo.Products.ToList()` is definitely a List
Kirk Broadhurst
You are correct Kobi. I know all about this stuff as *I* am indeed trying to parse the expression tree. Just a little more complex.
Schotime
Ok I now see what you are / were trying to achieve, I didn't understand that from the original question.
Kirk Broadhurst
A: 

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
i'm trying to pass the expression into plain text, and need to evaluate the p.Price into "30"
Schotime
+2  A: 

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
ahhhhh....I was one level off. Legend!!!
Schotime
If you had another level of nesting in the Object would anything change? eg. p.Product.Price
Schotime
@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
+2  A: 

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