[ASP.NET MVC]讓Html.RenderAction支持Lamda表達式


今天在ASP.NET MVC代碼時用到了Html.RenderAction,代碼如下:

@{Html.RenderAction("RecentNews")}

通過字符串指定Action的名稱,有兩點不爽:

1. 輸入時不能智能感知;

2. 輸錯了不能實時提示。

有這兩點不爽,寫代碼的樂趣就大減。有享受感覺的代碼應該是這樣的: 

@{Html.RenderAction<AggSiteController>(c => c.RecentNews());}

是的,Lamda,給你寫代碼帶來暢快感覺的Lamda!

微軟不讓我們享受,我們就自己動手,豐衣足食。自己寫一個支持Lamda表達式的Html.RenderAction,代碼如下:

復制代碼
using System.Web.Mvc;
using System.Web.Mvc.Html;
using System.Linq.Expressions;

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 actionName = ((MethodCallExpression)operation.Body).Method.Name;
            htmlHelper.RenderAction(actionName);
        }
    }
}
復制代碼

注:其中"((MethodCallExpression)operation.Body).Method.Name"代碼來自Get Method Name From Action

順帶分享一篇文章When to use Html.RenderPartial and Html.RenderAction in ASP.NET MVC Razor Views,通過這篇文章你可以清楚的知道Html.RenderPartial與Html.RenderAction之間的區別。

比如:博客園首頁的最新隨筆列表就適合用Html.RenderPartial,而右側的“新聞列表”就適合用Html.RenderAction。

簡單的理解就是:Html.RenderPartial用的到PartialView只用一次(雖然實際可以多次使用,但比較麻煩,每次都要傳Model),Html.RenderAction用的到PartialView被多個視圖使用(有自己的Action提供Model)。

在評論中,向晚提到了Html.RenderAction的一個優點:

ChildAciton的優勢是可以應用OutputCahce特性實現局部緩存。

我們當時用Html.RenderAction,而不用Html.RenderPartial,這也是一個重要原因。

總結一下ChildAciton+PartialView的優勢:

  1. 可以在“不同Action相同View”中方便地被共享
  2. 可以在“不同Action不同View”中方便地被共享
  3. “可以應用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)。

開源,真好! 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM