访问成员expression式的值

如果我有一个产品。

var p = new Product { Price = 30 }; 

我有以下linq查询。

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

在一个IQueryable提供程序中,我得到一个包含常量expression式的p.Price的MemberExpression,但是我似乎无法从中得到值“30”。

更新我已经尝试过,但它似乎并没有工作。

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

干杯。

你可以编译和调用一个lambdaexpression式,它的主体是成员访问:

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

parsingexpression式树时,局部评估是一种常用的技术。 LINQ to SQL在相当多的地方做这件事情。

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

常量expression式将指向由编译器生成的捕获类。 我没有包括决定点等,但是这里是如何得到30:

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

现在price30 。 请注意,我假定Price是一个属性,但实际上你会写一个处理属性/字段的GetValue方法。

qList<Product>的types。 该列表没有价格属性 – 只有单个产品。

第一个或最后一个产品将有一个价格。

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

如果你知道集合中只有一个,你也可以使用Single来压扁它

 q.Single().Price 

你可以使用以下内容:

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

使用Expression.Lambda(myParameterlessExpression).Compile().Invoke()有几个缺点:

  • .Compile() 。 即使对于小型expression片段,也可能需要几毫秒才能完成。 然而, Invoke -call的速度超快,对于简单的算术expression式或成员访问只需要几个纳秒。
  • .Compile()将生成(发出)MSIL代码。 这可能听起来很完美(并解释了优秀的执行速度),但问题是:代码占用内存, 在应用程序结束之前无法释放内存,即使GC收集了代理引用!

我们可以避免使用Compile()来避免这些问题,或者caching已编译的代表以重新使用它们。 我的这个小库提供了Expressions以及caching编译的 解释Expressions所有常量和闭包都被附加参数自动replace,然后重新插入到闭包中,并返回给用户。 这两个过程都经过了良好的testing,在生产中使用,都有其优点和缺点,但比Compile()快100倍以上,并避免内存泄漏!

你究竟想要完成什么?

因为要访问Price的价值,你必须做一些事情:

 var valueOfPrice = q[0].Price;