Imagine you’re building an ASP.NET MVC website which has some performance problems. I’m sure this would never actually happen to you, but imagine you’re facing this problem just for the sake of exploring the possibilities. :-)
Now, your web app is mostly fast and responsive, but there are certain types of data that just bring the whole thing to a grind. This article will cover a technique using ASP.NET MVC partial views, along with just a sprinkle of jQuery, JavaScript, and HTML5 to make your site feel nice and responsive even if you cannot increase the speed of certain operations.
First a disclaimer / warning. I’m going to show you how to make your site feel faster without speeding it up. If the users feel that it’s fast, then it’s fast enough. However, it might be better to try to just make it faster in the first place. This article assumes you have either tried or ruled out things like increasing the DB performance by adding the proper indexes, caching data where feasible, optimizing queries, etc. OK, assuming you’ve done as much as you can for pure performance, let’s get to improving the experience for the users.
Consider a basic website that shows data from several sources. In the sample case, it’s kind of an iGoogle landing page with different items: TODO, news, etc. In the screenshot below you can see there are several sets of information shown (my details, news, and most popular items).
It turns out that my details and the main page are actually pretty fast. The news and popular items are not. Yet, we are computing them all at once in the MVC action. Thus the whole page feels sluggish and slow. Here is the underlying controller action method.
This is a perfectly reasonable action method. But after doing some profiling we see that we have the following performance footprint:
10ms repository.GetUserItems() 500ms repository.GetNews() 2000ms repository.GetPopular() 2510ms TOTAL
I don’t know about you, but 2.5 seconds seems like an unacceptable page load time. Don’t think so? Check out these amazing facts:
Latency is Everywhere and it Costs You Sales – How to Crush it
Latency matters. Amazon found every 100ms of latency cost them 1% in sales. Google found an extra .5 seconds in search page generation time dropped traffic by 20%.
So a simple solution would be to break the loading of the slower sections (news & popular in our case) apart from the faster sections in the page. We can load the fast data immediately using direct model to razor processing and push the loading of the slow parts to an asynchronous operation using AJAX.
That might be a lot of work in general. But with MVC we can employ a few techniques and really make this simple and foolproof.
First, we’re in luck from the start because we are already using partial views (a key step) for our razor code. If you don’t have partial views, they are easy to add. Here is the relevant page section which renders the data synchronously.
MVC will easily serve the PopularControl’s content up over AJAX and same for any other partial view. So let’s change our controller action in the most simple way to allow us to ask for those elements independently. Here’s the new controller code:
It is important to note that we are returning PartialView (not View) for the parts that were previously managed in the view (news & popular).
Assuming the metrics above, the page will now load in 10ms. That’s vastly better than 2,500ms. With razor rendering time and other overhead it’s more like 50ms, but still vastly better.
But our CSHTML is now messed up. What do we need to do to load this content asynchronously? First, we start by punching “holes” in our razor page where the JavaScript can add the content async. Notice how we use the data-url attribute to simplify coordinating the proper location for the content. We’ll see the JavaScript side of things shortly.
Each section that will be loaded asynchronously now has three changes.
- We have a div with the class partialContents. We use this in jQuery to locate them.
- Each partialContents section has a data-url for the location of the partialview controller action.
- Each partialContents section has a message and image to show an AJAX indicator that we are working on that area. There is no extra effort to show this. It appears by default and will be replaced as the content loads.
So the controller is ready to serve up this partial content. The view is showing a pending action and has a place to put the results. The final step is to just add the smallest amount of jQuery to do the download and update.
Here is the JavaScript file that does the work. Notice we simply select all the partialContents sections on page load with jQuery. The foreach one we pull out the url and call jQuery.load(). This does the download and updates the HTML for the correct partialContents.
That’s it! Now our site loads the slow sections in parallel and asynchronously.
To fully appreciate the difference you should see it for yourself. Check out the live demo I’ve posted here:
Sample: Improve Perceived Performance with Async Partial Views
Slow Version:
http://samples.michaelckennedy.net/asyncviews/home
Async Version:
http://samples.michaelckennedy.net/asyncviews/async
Be sure to click between them with the nav in the top right. Download the code here:
AsyncPartialViewsSampleKennedy.zip
Finally, here is the original version with the timing measured in FireBug:
Notice the time: 2.6 seconds. Yuck. Here is the timing after the improvement:
Now it’s 50ms! That is pretty awesome. Of course, if you look at the AJAX requests, they still take the same amount of time. But we do get the added benefit of natural parallelism from AJAX:
Now the whole page is ready in around 2 seconds which is in some sense truly faster (not just perceived) than the original.
So to recap, here are the steps we performed.
- Identified the segments of data that are slow.
- Took the original action method and change it to only load the fast content in the controller.
- Moved the CSHTML responsible for rendering each slow section to a partial view if it was not already done.
- Added an action method returning a PartialViewResult for each async section.
- Added a partialContent pending section with loading message and data-url to parts of the original page which are going to be loaded asynchronously.
- Use jQuery to find and call .load() for each section.
That’s it. Hopefully you will find this a useful technique for your slower pages.
Cheers,
@mkennedy










Pingback: Dew Drop – November 14, 2012 (#1,442) | Alvin Ashcraft's Morning Dew
I like that solution. Thank you.
You’re welcome! Thanks for the feedback.
Pingback: The Morning Brew - Chris Alcock » The Morning Brew #1233
Nice clean article
Thanks Paul!
Nice and clear article Michael. I did something similar to this in a page that included widgets where the page widgets references were stored in a database. Basically the initial page viewmodel has a list of the widgets and then I use AJAX to load them dynamically. I might incorporate your ‘loading …’ feature to make it look a little slicker. Thanks.
Hi Richard. Thanks. Glad you could take something away from the article to help.
Cool. You should also make the data source calls (be they EF or WebServices, or whatever) use async and await as they are so long running.
Hey Scott!
Thanks for the comment. You’re absolutely right. While we’re busy parallelizing the requests from the client, the partial view action methods I was hitting with Ajax could easily have been decorated with async / await. Good advice.
I don’t get what do we gain by using async on server side when we already call it asynchronously from client.
Nice article. I just did a similar dashboard, and now profiling EF and making optimizations. Most of my performance issues were usually related with “SELECT N+1″ queries.
But even if I make optimizations to each part, the sum effect of all will be significant. I will probably use your approach for all the parts, not just the slow ones.
Glad you liked it. I agree that you should look to obvious perf issues first before making them just run in the background.
BTW, if possible, look at Scott’s recommendation (previous comment). If you can use async and await as you wait on the DB you’ll be even better off from a scalability perspective (won’t affect single user perf much).
Cheers,
Michael
Very good and simple solution. If you need those partially loaded sections to be toggled by the user into edit mode, then check out the solution I blogged about here:
http://candordeveloper.com/2012/07/19/asyncui-mvc-with-progressive-enhancement/
This is exactly what I was looking for! I am sort of surprised that the default behavior of Html.Partial() and Html.RenderAction() are not asynchronous.
Glad to help Scott!
Michael,
Perhaps a stupid question, but how would you handle calling the partial view methods with a parameter? E.g. let’s say the News method was changed to News(string subscriberId).
Hi,
You can leverage url route data or query strings and model binding for must data. For example, if you want pass a category to the news action, then:
would pass 72 to the action. For more complex data, use query strings. For example, to pass a class such as:
class NewsViewModel {
public string Category {get; set;}
public string SortBy {get; set;}
public int? Page {get; set;}
}
you can just use:
Does that help?
Cheers,
Michael
I believe something got “lost in translation” here ;-)
Right, sorry! The html I put in there was entered AS HTML because I”m the editor on the blog. Sorry. think angle bracket when you see [div] etc.
First example:
[div data-url="/home/news/72" ][/div]
Second example:
[div data-url="/home/news?Category=recent&SortBy=views&Page=2" ][/div]
Hello,
Can’t an AsyncController be used for these partials?
Thank You,
Miguel
AsyncController works great with heavy loaded web servers. It doesn’t add parallelism. [It provides better using of IIS threads.]
Yes, you can use AsyncControllers as Scott Hanselman was commenting above (async & await is the new way). But that doesn’t significantly increase the load time. It does parallelize it but in my example this would drop the load time from 2.6s to 2.0s. The technique in the article takes it from 2.6s to 0.05s.
Pingback: Links of the week | Jan @ Development
It doesn’t work with enabled Session. If Session is enabled (by default) then all requests in Session are executed consequentially and performance will be same time + time to round-trip for each ajax call.
Code sample below shows how to prepare data in parallel:
await Task.WhenAll(
() =>
{
var repository = Factory.Create(); // get repository
items = repository.GetUserItems();
},
() =>
{
var repository = Factory.Create();
news = repository.GetNews();
},
() =>
{
var repository = Factory.Create();
popular = repository.GetPopular();
});
I like this , it look nice way to populate data in parallel way
Thanks!
@Entity Framework doesn’t support async operations yet.
It looks like EF6 will implement this feature.
Anyway, I like this approach, when the content is not important for search engines.
Thanks. Yes, we’ll have to wait for EF 6 (which shouldn’t be too long) for async & await.
Your point about SEO and this content is totally right. It’s basically invisible to Google and Bing.
Unless it is necesary to have any new content available immediately on every page hit, would it be faster to cache these slow page elements.
I totally agree, which is why I added this as one of the first paragraphs:
First a disclaimer / warning. I’m going to show you how to make your site feel faster without speeding it up. If the users feel that it’s fast, then it’s fast enough. However, it might be better to try to just make it faster in the first place. This article assumes you have either tried or ruled out things like increasing the DB performance by adding the proper indexes, caching data where feasible, optimizing queries, etc. OK, assuming you’ve done as much as you can for pure performance, let’s get to improving the experience for the users.
This is awesome! Thanks Mike.
You’re most welcome, thanks for saying so!
Pingback: Improved Performance of Websites Using Asychronous Partial Views | Best Windows Web Hosting
Nobody has talked about the possible impact of this technique regarding SEO as the bot don’t see the lazy loaded content, I’d use this for content not to be indexed by search engines, or in web applications where nothing should be indexed. Thanks for sharing!
Hi Abel,
A very good point I overlooked in the article. My original use-case was for an internal app so SEO wasn’t top of mine. But yes, if the content needs to be found via search you’ll need to do a little extra work to provide something. But the async stuff won’t be indexed.
Neat solution. Thanks. Have an idea tho. Wouldn’t it be nice to turn this into a HtmlHelper extensions method to keep it as simple as possible? Something like this. Html.PartialAjax or PartialAsync (which could be misleading given async keyword). Are there any cons?
Thanks!
This is awesome!
thank you man
Thanks!
I like your solution. It is awesome !
Good One…
Thanks
Nice clean Article! Liked the way it was presented.
Thanks!
I am beginner to use MVC architecture , Will you please make me to understand what is the need of repository’s,Please mail me if you have related content on MVC (Material).
Excellent article Michael. Thanks a lot.
Let’s try again… (Re-post!)… First of all, I thought It is a great article… Only wished I had found it sooner! Now I’m trying to load and run your sample code but it’s trying to force me to download IIS Express. I’d rather if possible use the build-in web server that runs when you run and mvc app in .net or use the regular IIS which I already have on my pc. Is there a way to disable this? I’ve tried setting up UseIIS to false in the .csproj file but it didn’t like that! Any ideas?
Many thanks.
Hi,
Thanks! And you should be able to run the project without IIS express. Just go to the project properties, web tab and choose development web server rather than IIS express.
Regards,
Michael
Hi, Thanks for the reply. I tried what you suggested, but it just wouldn’t let me load the project unless I accepted to download IIS Express, so I gave in ;) .. All is still working, so no worries.
Great article but is there a way to run the project without IIS express. Thanks.
definitely use this – thx kesar
Awesome blog…. very useful
Simple and efficient. Adopted !
Michael, any thoughts on how to make the jquery load call happen only when the div’s are in view for the user’s screen? IE. if it is below the fold then don’t load the data/html.
I am using a jquery plugin to lazy load some images but this same idea would be great to use on the div elements, etc.
Hi,
Yes, I think that should be totally doable. Check out this jquery plugin (not tested):
http://imakewebthings.com/jquery-waypoints/
Basically lets your register for when content is scrolled into view rather than document.ready(). I think you could move the code over to these events and pretty much reuse the same idea here.
Cheers,
Michael
Hi Michael,
Is there a way to use this mechanism in conjunction with an actionlink and passing a parameter to it? I want to have a few with a list of items and when the user clicks on one of the item listed, I want to display the data for this item in a view on the right inside? Thanks.
Hi,
Yeah, totally. But I don’t think you need actionlink (although you could use it). Where I had data-url=”/some/url” just pass the data there. Either by constructing the URL explicitly or using Url.ActionLink().
Cheers,
Michael
Pingback: Improving perceived performance in a CRM - Gold Coast - Site Home - MSDN Blogs
I very much like your approach. I was looking for something similar, though my requirement is bit different. I am going to try your way Nd if this works, you are my super hero.
Cheers,
Jay
Great exapmle. Here is the russian version http://www.codehint.ru
Mmmh… why is the razor version 5 times slower?
Damn. I really like razor, but it shows more and more performance flaws…
Hi,
I think you misunderstood. They are BOTH razor – basically identical code. One uses the async technique, the other does not.
Cheers,
Michael
Hi!
This is a nice article. Thank for sharing your knowledge. There are some other links related to “Calling partial view using Ajax in ASP.NET MVC 4″. I hope this is a
very useful for developers.
http://www.mindstick.com/Articles/8e131adf-2621-4cd7-8557-770c1ede799c/?Calling%20partial%20view%20using%20Ajax%20in%20ASP.NET%20MVC%204
http://ciintelligence.blogspot.in/2012/06/aspnet-mvc-ajax-load-with-partial-view.html