访问成员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);
现在price
是30
。 请注意,我假定Price
是一个属性,但实际上你会写一个处理属性/字段的GetValue
方法。
q
是List<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;