2010-04-11 44 views
54

如果我有產品。訪問成員表達式的值

var p = new Product { Price = 30 }; 

並且我有以下linq查詢。

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

在一個IQueryable供應商,我得到一個MemberExpression回來含有常量表達式的p.Price,但我似乎無法得到值「30」,從回來。

更新 我試過這個,但它似乎沒有工作。

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

乾杯。

回答

84

您可以編譯和調用lambda表達式體爲成員訪問:

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

本地評價是一種常見的技術解析表達式樹的時候。 LINQ to SQL在很多地方都能做到這一點。

+0

獲取此錯誤類型爲「System.Double」的表達式不能用於返回類型爲「System.Object」的解析爲double在我使用的例子中。 – Schotime 2010-04-11 13:12:16

+2

不得不添加: var expression = Expression.Convert(member,typeof(object));在函數的第一行用雙重轉換修復上述錯誤! – Schotime 2010-04-11 14:23:58

+0

啊,是的,我有時會忘記你必須明確C#隱含的表達式樹(比如轉換)。我很高興這對你有用。 – 2010-04-11 18:18:30

1

q的類型爲List<Product>。該清單沒有價格屬性 - 只有單個產品。

第一個或最後一個產品將有一個價格。

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

如果你知道有一個僅使用在收集你也可以將其壓平單

q.Single().Price 
+1

哈啊....沒有。 repo.Products()返回一個IQueryable 。 – Schotime 2010-04-11 10:07:32

+0

是的,但是最後的'.ToList()'使它成爲一個列表。 – 2010-04-11 10:26:52

+1

無論是List還是IQueryable,您仍然可以使用First,Last或Single - 但不會犯任何錯誤,'repo.Products.ToList()'絕對是一個列表 – 2010-04-11 10:28:10

0

而且正是你想實現什麼?

因爲訪問的Price的價值,你必須做一些事情,如:

var valueOfPrice = q[0].Price; 
+0

我試圖將表達式轉換爲純文本,並且需要將p.Price評估爲「30」 – Schotime 2010-04-11 10:10:16

1

您可以使用以下方法:

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

這將起作用,但如果不需要這樣做會很好。 Linq-2-Sql是否支持我試圖實現的語法? – Schotime 2010-04-11 12:12:19

23

常量表達式將會指向捕獲類由編譯器生成。我不包括決策點等等,但這裏是如何拿到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

+0

ahhhhh ....我是一個關卡。傳說!!! – Schotime 2010-04-11 12:29:38

+0

如果你在對象中有另一個層次的嵌套會有什麼變化?例如。 p.Product.Price – Schotime 2010-04-11 12:58:57

+3

@Schotime - 的確如此。要以通用方式處理此問題,請參閱此處的「評估」和「TryEvaluate」:http://code.google.com/p/protobuf-net/source/browse/trunk/protobuf-net.Extensions/ServiceModel/Client/ ProtoClientExtensions.cs – 2010-04-11 15:06:29

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

獲取結果的最快,最簡潔的方式。 – iggymoran 2012-10-09 15:41:26

+1

不能相信涉及「DynamicInvoke」的東西可以*最快* @iggymoran你測試過嗎?或者你的意思是打字最快? ;) – nawfal 2013-06-04 16:52:14

+0

最快的類型和最容易理解到底發生了什麼。 DynamicInvoke最終使用反射來執行它,並不是世界上最快的事情。Bryan Watts的答案通過獲得func並執行(通過調用)來解決這個問題。當我第一次遇到這個答案時,它更容易理解發生了什麼。 – iggymoran 2013-06-06 08:38:16

1

使用Expression.Lambda(myParameterlessExpression).Compile().Invoke()有幾個缺點:

  • .Compile()。即使對於小型表達片段,也可能需要幾毫秒才能完成。然而,Invoke-調用之後超快,對於簡單的算術表達式或成員訪問只需要幾個納秒。
  • .Compile()將生成(發出)MSIL代碼。這可能聽起來很完美(並解釋了出色的執行速度),但問題是:代碼佔用內存,在應用程序完成之前無法釋放,即使GC收集了委託引用!

可以完全避免Compile()以避免這些問題或緩存已編譯的代表重新使用它們。 This我的小庫提供解釋Expressions以及緩存編譯,其中表達式的所有常量和閉包自動替換爲附加參數,然後重新插入閉包中,然後返回給用戶。這兩個流程都經過良好測試,用於生產,兩者都有優點和缺點,但比Compile()快100倍以上 - 並避免內存泄漏!

0

如果你有一個類:

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

和對象的實例:

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

var myItem = new Item { Id = 7 }; 

您可以用下面的代碼中使用表達得id的值

值將包含「7」