解析 Expression tree 時正確取得 ConstantExpression 中的 Value
最近在實作IQueryable的時候,遇到了一個問題
Expression<Func<object>> exp = () => "CodingRoad" ;
如上Code,其結構為:
是一個LambdaExpression,其Body是一個ConstantExpression
當要擷取ConstantExpression的值時,只要將其轉型就可取到
var value = (exp.Body as ConstantExpression).Value;
但是當傳入的是一個區域變數,或是一個物件的屬性等..如:
string str = "CodingRoad";
Expression<Func<object>> exp = () => str ;
此時要取出值,就比較麻煩了。
在編譯時,編譯器會自己建立一個Class,將傳入的str包起來
因此此時Expression的結構會變為
此時的Body是一個MemberExpression,就算再往下抓到ConstantExpression
var memberExp = (exp.Body as MemberExpression);
var value = (memberExp.Expression as ConstantExpression).Value;
其值也是 "UserQuery+<>c__DisplayClass0",而不是預期的"CodingRoad"
此時如果要取出值的話,必須使用反射(Reflection)將欄位的值取出
var memberExp = (exp.Body as MemberExpression);
var constabtExp = (memberExp.Expression as ConstantExpression);
var value = constabtExp.Type.GetFields().First().GetValue(constabtExp.Value);
或是將MemberExpression轉為LambdaExpression,Compile後並執行取得
Expression<Func<object>> exp = () => str ;
var memberExp = (exp.Body as MemberExpression);
var value = Expression.Lambda(memberExp).Compile().DynamicInvoke();
兩種方法皆可,但要看其Tree解析到哪裡。
如果是到MemberExpression的話,則兩種皆可用。
但如果已經到ConstantExpression,就只能用第一種了。
備註:
最近自己實作了一個Linq To GoogleImage
可用下列語法,查出所要的圖片
GoogleServicesContext context = new GoogleServicesContext();
var model = context.GoogleImages.Where(p => p.Name == "101" &&
p.PicType == FileType.JPG &&
p.Safe == SafeType.Off &&
p.Domain == "flickr.com" );
範例網站:
實作內容是解析ExpressionTree,將條件轉換成Google Image Search API 的參數
查詢後將結果回傳。
如果對此內容有興趣的可以一起討論討論。
而完整的Code等我重構到滿意之後再來發系列文分享。