续篇:新型Lamda版Html.RenderAction
在前一篇博文中,我们实现了一个简单的支持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)。
开源,真好!