Background
In ASP. in net mvc, the extension method of HtmlHelper RenderPartial brings great convenience for us to use UserControl. When we specify a UserControl, RenderPartial will find the corresponding UserControl in the current View folder, if not found, it will be searched in the Shared folder. Then there are some performance considerations when using the RenderPartial method, which deserves our attention.
Suppose there is a scenario where an article has many comments and we need to present a comment list on the page. Naturally, we will define a comment UserControl, as shown in the following code:
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>
The comment list is displayed on the page, and the code is very simple. It just traverses all the comments:
Code 2:
<div> <% foreach (var comment in Model.Comments) { Html.RenderPartial("CommentsItem", comment); } %> </div>
The result after running is as follows. You can see that the comment is correct:
Performance Optimization 1
However, when we output 200 comments at the same time, it takes a lot of time. When we use Stopwatch to measure it, we will find that it takes about 200 ms to output comments, that is to say, the average output of each comment is 1 ms. Let's take a look at the source code of ASP. net mvc. When searching for UserControl in WebFormViewEngine, follow the following search mode:
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;}
So if we specify the full path of UserControl in the RenderPartial method, can this search process be avoided? The Code for modifying Code 2 is as follows:
Code 4:
<div> <% foreach (var comment in Model.Comments) { Html.RenderPartial("~/Views/Shared/CommentsItem.ascx", comment); } %> </div>
Now, we can test that the average time for presenting 200 comments is kept at around 10 ms, which is nearly 200 ms higher than the previous method. But have we found a solution to the problem? ASP. net mvc does not cache the View path to be searched? With such a question, we find the code in the constructor of VirtualPathProviderViewEngine of ASP. net mvc source code:
Code 5:
protected VirtualPathProviderViewEngine() { if (HttpContext.Current == null || HttpContext.Current.IsDebuggingEnabled) { ViewLocationCache = DefaultViewLocationCache.Null; } else { ViewLocationCache = new DefaultViewLocationCache(); }}
If the Debug mode is enabled, NullViewLocationCache is used, that is, no cache is used. Otherwise, defaviewviewlocationcache is used to cache the View path. Therefore, the above test results are based on the Debug mode:
Code 6:
<compilation debug="true">
What should I do if I disable the Debug mode? Use the following code to disable the Debug mode:
Code 7:
<compilation debug="false">
If you perform another test, you will find that the average time spent on presenting 200 comments using Code 2 is about 10 ms. Therefore, when using the RenderPartial method, you do not need to specify the full path of UserControl to Improve the Performance, ASP. net mvc has done all this for us. All we need to do is to disable the Debug mode when releasing it to the production environment! In this example, the performance gap between enabling and Disabling Debug mode in one call is shown in:
Performance Optimization 2
Looking back at the previous code, we didn't actually do any performance optimization. I just wanted to remind you of it. In Code 2, our traversal Code is placed on the home page, that is, the RenderPartial method is called in each iteration, even though ASP. net mvc caches the UserControl path in RenderPartial, but 200 calls still have a large cost. What if we put our traversal code in UserControl and only call the RenderPartial method once on the home page? Modify UserControl to the following code:
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><%} %>
In this way, only one RenderPartial call is performed on the home page, as shown in the following code:
<div> <% Html.RenderPartial("CommentsItem", Model.Comments); %> </div>
Test again. We can see that it takes less than 1 ms to display 200 comments! For the sake of intuition, we still use the following graphical representation:
We can see that the RenderPartial method calling is reduced by traversing in UserControl, which brings a considerable performance improvement.
Summary
This article focuses on ASP. net mvc when using the RenderPartial method, remember two points: first, in ASP. when the net mvc application is released to the production server, do not forget to disable the Debug mode (for ASP.. NET WebForm applications); second, minimize the number of calls to the RenderPartial method, such as traversing in UserControl. I hope it will be useful to you.