解析 Expression tree 時正確取得 ConstantExpression 中的 Value

解析 Expression tree 時正確取得 ConstantExpression 中的 Value

最近在實作IQueryable的時候,遇到了一個問題

Expression<Func<object>> exp = () => "CodingRoad" ;

如上Code,其結構為:

image

是一個LambdaExpression,其Body是一個ConstantExpression

當要擷取ConstantExpression的值時,只要將其轉型就可取到

var value = (exp.Body as ConstantExpression).Value;

 

但是當傳入的是一個區域變數,或是一個物件的屬性等..如:

string str = "CodingRoad";
	
Expression<Func<object>> exp = () => str ;

此時要取出值,就比較麻煩了。

在編譯時,編譯器會自己建立一個Class,將傳入的str包起來

image

因此此時Expression的結構會變為

image

此時的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" );

範例網站:

image

實作內容是解析ExpressionTree,將條件轉換成Google Image Search API 的參數

查詢後將結果回傳。

 

如果對此內容有興趣的可以一起討論討論。

而完整的Code等我重構到滿意之後再來發系列文分享。