ASP.NET MVC Performance Tips(1):RenderPartial效能最佳化

來源:互聯網
上載者:User
背景

在ASP.NET MVC中,HtmlHelper的擴充方法RenderPartial為我們使用UserControl帶來了極大的方便,當我們指定一個UserControl時,RenderPartial會在當前View檔案夾下尋找相應的UserControl,如果沒有找到則會到Shared檔案夾下尋找。然後在使用RenderPartial方法有一些效能方面的考慮,值得我們去關注。

設想有這樣一個情境,一篇文章有很多個評論,在頁面中我們需要呈現出一個評論列表,自然我們會定義一個評論的UserControl,如下代碼所示:

Code 1:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<Comment>" %><%@ Import Namespace="TerryLee.MvcPerformance01.Models" %><p>    ID: <%=Model.ID %> <br />    Author: <%=Model.Author %> <br />    Description: <%= Model.Description %></p><hr />

在頁面中呈現評論列表,代碼非常簡單,僅僅是遍曆所有的評論而已:

Code 2:

<div>   <%     foreach (var comment in Model.Comments)     {         Html.RenderPartial("CommentsItem", comment);     }  %> </div>

運行後效果如下,可以看到正確的輸出了評論:

 

效能最佳化1

然而當我們同時輸出200條評論的時候, 卻要花費大量的時間,用Stopwatch來測量一下會發現,輸出200條評論花費的時間基本在210ms左右,也就是說平均每條評論的輸出花費了1ms。我們不妨看一下ASP.NET MVC的原始碼,在WebFormViewEngine中當尋找UserControl時,遵循如下尋找模式:

Code 3:

public WebFormViewEngine() {    MasterLocationFormats = new[] {        "~/Views/{1}/{0}.master",        "~/Views/Shared/{0}.master"    };    ViewLocationFormats = new[] {        "~/Views/{1}/{0}.aspx",        "~/Views/{1}/{0}.ascx",        "~/Views/Shared/{0}.aspx",        "~/Views/Shared/{0}.ascx"    };    PartialViewLocationFormats = ViewLocationFormats;}

所以如果我們在RenderPartial方法中,指定了UserControl的完全路徑,是不是可以避免這個尋找過程呢?修改Code 2代碼如下所示:

Code 4:

<div>   <%       foreach (var comment in Model.Comments)       {           Html.RenderPartial("~/Views/Shared/CommentsItem.ascx", comment);       }     %> </div>

現在再測試一下會發現呈現200條評論的時間平均值保持在10ms左右,比前面的方式提升了近200ms。然而我們是否真的找到瞭解決問題的方法?ASP.NET MVC難道不對尋找的View路徑進行緩衝?帶著這樣的疑問,我們在ASP.NET MVC原始碼VirtualPathProviderViewEngine的建構函式中,找到這樣一段代碼:

Code 5:

protected VirtualPathProviderViewEngine() {    if (HttpContext.Current == null || HttpContext.Current.IsDebuggingEnabled) {        ViewLocationCache = DefaultViewLocationCache.Null;    }    else {        ViewLocationCache = new DefaultViewLocationCache();    }}

這裡的判斷說明如果啟用了Debug模式,將會使用NullViewLocationCache,即不進行緩衝,否則會使用DefaultViewLocationCache對View路徑進行緩衝。所以上面的測試結果都是基於Debug模式:

Code 6:

<compilation debug="true">

如果關閉了Debug模式,測試結果又該如何呢?使用下面代碼關閉Debug模式:

Code 7:

<compilation debug="false">

再次進行測試,會發現使用Code 2代碼呈現200條評論時,花費的時間平均值也是在10ms左右。所以在使用RenderPartial方法時,大可不必為了提升效能而指定UserControl的完全路徑,ASP.NET MVC已經為我們做好了這一切,我們要做的僅僅是在發布到生產環境時,別忘了關閉Debug模式!在本樣本中,開啟Debug和關閉Debug模式在一次調用時的效能差距如所示:

 

效能最佳化2

現在回過頭來看前面的代碼,其實並沒有做什麼效能最佳化,僅僅時給大家提個醒而已。在Code 2中,我們的遍曆代碼放在了首頁面中,即在每一次迭代中調用RenderPartial方法,儘管ASP.NET MVC在RenderPartial時,對於UserControl路徑做了緩衝,但是200次的調用仍然有不小的開銷。如果我們的遍曆代碼放在UserControl中,而在首頁面中只進行一次調用RenderPartial方法,結果又將如何呢?修改UserControl為下代碼所示:

Code 8:

<%@ Control Language="C#" Inherits="System.Web.Mvc.ViewUserControl<IList<Comment>>" %><%@ Import Namespace="TerryLee.MvcPerformance01.Models" %><%    foreach(Comment comment in Model)    { %><p>    ID: <%= comment.ID%> <br />    Author: <%= comment.Author%> <br />    Description: <%= comment.Description%></p><%} %><hr />

這樣在首頁面中,只進行一次RenderPartial調用,如下代碼所示:

<div>   <%     Html.RenderPartial("CommentsItem", Model.Comments);   %> </div>

此時再次測試,可以看到呈現200條評論所花費的時間不足1ms!為了直觀期間,我們仍然使用圖形表示如下:

 

從中可以看到,通過在UserControl中進行遍曆,減少RenderPartial方法調用,帶來的效能提升還是非常可觀的。

總結

本文主要討論在ASP.NET MVC中使用RenderPartial方法時的一些效能問題,記住兩點:一是在ASP.NET MVC應用程式發布到生產伺服器時,別忘了關閉Debug模式(對於ASP.NET WebForm應用程式也是一樣);二時儘可能的減少調用RenderPartial方法的次數,如通過在UserControl中進行遍曆等方法。希望對大家有用。

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.