表達式樹是LINQ To everything 的基礎,同時各種類庫的Fluent API也 大量使用了Expression Tree。還記得我在不懂expression tree時,各種眼花缭亂的API 看的我各種膜拜,當我熟悉expression tree 後恍然大悟,不用看代碼也能知道别人的API 是如何設計的(^_^)。 接下來這篇部落格就談談如何使用expression tree擴充MVC中的HtmlHelper和UrlHelper。
場景
當我們在MVC中生成一個action的url會這樣寫:var url=UrlHelper.Action("index", "Home"); 如果要render一個action時會這樣寫:Html.RenderAction("index", "Home");
這樣的寫法瑕疵在于我們傳遞了兩個字元串類型的參數在代碼中,而我們又免不了對action和controller做重命名操作:index->default, 即便是你用resharper這樣的工具重命名也無法将UrlHelper.Action("index", "Home"); 改變為UrlHelper.Action("default", "Home");
vs甚至在編譯時都不會檢查出來這個錯誤。 是以我們的目标是:設計出具有靜态檢查的API,讓vs 提示出這個錯誤來,甚至是重命名時直接把相關代碼都能重命名。
使用Expression Tree 重新設計這兩組API
目标:設計出類似的API:Url.Action((HomeController c) => c.Index());
1.很明顯我們需要在UrlHelper上寫個擴充方法:
現在隻需要根據表達式Expression<Func<TController, ActionResult>> actionSelector 解析出action,controller,還有routeValues即可
2.解析出controller 的名稱
分析:controller的名稱可以根據泛型方法中的泛型參數TController得到
3.解析action的名稱
分析:由于表達式Expression<Func<TController, ActionResult>> actionSelector 是一個MethodCallExpression, 是以可以很容易得到action的名稱:var action=call.Method.Name;
這樣已經完成了最簡單的url構造方案, 但是我們還沒有處理帶有參數的action類型,例如:Url.Action((HomeController c) => c.Detail(10, 20));
4.解析routeValues
分析:action中的參數實際上就是MethodCallExpression中的參數,我們解析這個expression的參數即可,然後得到RouteValues
如此一來,類似Url.Action((HomeController c) => c.Detail(10, 20));這樣的action也可以構造出Url了。
此代碼并不支援複雜類型的參數,對于action中傳入複雜的類型,比如:
如果Action中的參數使用了User類型:
如何解析呢?
大功告成,現在已經完美解決了各種類型的參數傳入。
同樣的道理,我們可以擴充HtmlHelper 的 RenderAction(), ActionLink()….
缺陷
早在09年,jeffery zhao就發表了lambda方式生成url的部落格,對比了幾種方案的性能問題,并且給出了優化方案,當然,我在寫這篇部落格的時候還沒有真正嘗試去優化這個方案,隻是再次拜讀了大神的方案,記得早些年就讀過這些文章,但是今天重新讀過仍然獲益匪淺,不由得感歎幾句,莫非跑題了;-);-)
接下來我會思考這個優化的問題。
作者:Richie Zhang
來源:http://www.cnblogs.com/richieyang/
聲明:本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。