在前一篇博文中,我們實現了一個簡單的支持Lamda表達式的Html.RenderAction。
在評論中,向晚提到了Html.RenderAction的一個優點:
ChildAciton的優勢是可以應用OutputCahce特性實現局部緩存。
我們當時用Html.RenderAction,而不用Html.RenderPartial,這也是一個重要原因。
總結一下ChildAciton+PartialView的優勢:
- 可以在“不同Action相同View”中方便地被共享
- 可以在“不同Action不同View”中方便地被共享
- “可以應用OutputCahce特性實現局部緩存”
我們在解決另外一個問題時,發現它還有一個優勢:
4. 可以用ChildAciton直接處理Ajax請求。
比如博客園首頁加載“最新隨筆列表”的應用場景,在用戶打開頁面時直接顯示最新隨筆(非ajax加載);在用戶點擊頁面上的“刷新”鏈接時,通過ajax更新“最新隨筆列表”。
使用ChildAciton+PartialView,在首頁頁面視圖中只需Html.RenderAction即可。
@{ Html.RenderAction<AggSiteController>(c => c.PostList(1,20)); }
同樣的ChildAction可以直接服務於Ajax請求,實現了ChildAciton+PartialView的重用。
這時,我們遇到了一個問題,在RenderAction的時候需要向Action傳遞參數,之前實現的簡陋的Html.RenderAction並沒有對此提供支持,需要改進一下。
從testzhangsan的評論中獲知,ASP.NET MVC 4中已經在實現Lamda版Html.RenderAction。簽出ASP.NET MVC 4的源代碼一下,果然實現了,向Action傳參數的問題自然也被微軟解決了。參照那段代碼,精簡一下就能解決我們的問題。
向Action傳參數,需要通過RouteValueDictionary,我們所要做的工作就是從Lamda表達式中獲取參數名稱與參數值,並還添加至RouteValueDictionary。
完整代碼如下:
using System.Web.Mvc; using System.Web.Mvc.Html; using System.Linq.Expressions; using System.Web.Routing; namespace System.Web.Mvc.Html { public static class HtmlHelperExtensions { public static void RenderAction<TController>(this HtmlHelper htmlHelper, Expression<Action<TController>> operation) where TController : Controller { var controllerName = typeof(TController).Name; if (controllerName.EndsWith("Controller")) { controllerName = controllerName.Substring(0, controllerName.Length - "Controller".Length); } var call = operation.Body as MethodCallExpression; if (call != null) { var actionName = call.Method.Name; var parameters = call.Method.GetParameters(); if (parameters.Length > 0) { var routeValues = new RouteValueDictionary(); for (int i = 0; i < parameters.Length; i++) { var ce = call.Arguments[i] as ConstantExpression; if (ce != null) { routeValues.Add(parameters[i].Name, ce.Value); } else { var lambda = Expression.Lambda(call.Arguments[i], operation.Parameters); Delegate d = lambda.Compile(); var value = d.DynamicInvoke(new object[1]); routeValues.Add(parameters[i].Name, value); } } htmlHelper.RenderAction(actionName, controllerName, routeValues); } else { htmlHelper.RenderAction(actionName, controllerName); } } } } }
注:目前的這個實現不支持可空類型的參數,比如:public ActionResult PostList(int? pageIndex)。
開源,真好!