Boost ASP.NET performance with deferred content loading
AJAX, ASP.NET, JavaScript, Performance, UI By Dave Ward on February 5th, 2008
When rolled into the page life cycle of an ASP.NET WebForm, that red bar is one of your application’s greatest enemies. No matter how well you optimize the rest of the page, even one slow task can become the sole factor determining a user’s perception of the entire page’s performance.
In this post, I’m going to show you one way to circumvent that problem. By placing ancillary content in user controls and delaying their load until the core page content has been displayed, you can drastically improve perceived performance.
When broken down into digestible chunks, the technique is easy to implement and lends your application a level of polish that your users are sure to appreciate. The four steps required to accomplish this will be: building the user control, statelessly rendering the control as HTML, providing progress indication, and using ASP.NET AJAX to request and inject that HTML.
Building the user control
First, we need some slow-loading, auxiliary content to encapsulate in a user control. For this example, that’s going to be a minimal RSS feed reader widget that displays the most recent posts from this site.
The key activity here is retrieving my RSS feed and querying it for some basic information. To expedite this, I’m going to use one of ASP.NET 3.5’s great new features: LINQ to XML. LINQ really makes short work of this normally tedious task. It still blows me away every time I use it.
protected void Page_Load(object sender, EventArgs e) { XDocument feedXML = XDocument.Load("http://feeds.encosia.com/Encosia"); var feeds = from feed in feedXML.Descendants("item") select new { Title = feed.Element("title").Value, Link = feed.Element("link").Value, Description = feed.Element("description").Value }; PostList.DataSource = feeds; PostList.DataBind(); }
To display this, I’m going to use another of ASP.NET 3.5’s new features, the ListView control:
<asp:ListView runat="server" ID="PostList"> <LayoutTemplate> <ul> <asp:PlaceHolder runat="server" ID="itemPlaceholder" /> </ul> </LayoutTemplate> <ItemTemplate> <li><a href='<%# Eval("Link") %>'><%# Eval("Title") %></a><br /> <%# Eval("Description") %> </li> </ItemTemplate> </asp:ListView>
With a little bit of CSS (included in the source download later), this results in something resembling the screenshot to the right.
The ListView comes in especially handy for our purposes here, because it gives you such effortless control over the rendered HTML. When injecting generated HTML into a page, you really appreciate knowing exactly what markup to expect.
Rendering the user control as HTML
The next step is to create a web service that statelessly renders our user control as an HTML string. By statelessly, I mean that we need to render this user control outside the context of an active ASP.NET Page instance, where user controls are normally intended to be used.
To solve that problem, you can create a temporary instance of the ASP.NET Page class, dynamically add the user control to it, and then execute it within the web service’s context. Doing this turns out to be easier than it is to accurately describe:
[WebMethod] public string GetRSSReader() { // Create a new Page and add the control to it. Page page = new Page(); UserControl ctl = (UserControl)page.LoadControl("~/RSSReaderControl.ascx"); page.Controls.Add(ctl); // Render the page and capture the resulting HTML. StringWriter writer = new StringWriter(); HttpContext.Current.Server.Execute(page, writer, false); // Return that HTML, as a string. return writer.ToString(); }
You might worry about creating an entire Page instance for what is supposed to be a performance enhancing technique. At first, I shared the same concern.
However, since our Page instance is created outside of the normal ASP.NET HTTP pipeline and only contains one control, the overhead is negligible. The Page itself is fairly performant compared to all of the other work involved in a typical HTTP round-trip.
Setting up the demonstration page
To demonstrate, we’ll need a page with some fast-loading content to provide contrast. Inside that, we can embed an empty DIV which will be used to accurately inject the user control’s rendered HTML.
<asp:ScriptManager runat="server"> <Services> <asp:ServiceReference Path="~/RSSReader.asmx" /> </Services> <Scripts> <asp:ScriptReference Path="~/Default.js" /> </Scripts> </asp:ScriptManager> <div id="Container"> <div id="RSSBlock" class="loading"></div> <div id="Content"> <p>Lorem ipsum dolor sit amet, consectetuer adipiscing...</p> </div> </div>
As you can see, the RSSBlock DIV is initially assigned a CSS class of “loading”. This is the CSS for the loading class:
.loading { background: url('progress-indicator.gif') no-repeat center; }
What this does is give us a bit of rudimentary progress indication. Until we update it later, it will display an empty placeholder with an indicator.
You and I know that it’s a bit of a scam, but your users will never realize the progress indicator isn’t real. I won’t tell if you won’t!
Calling the web service from JavaScript
Now that we’ve got a place to inject it, the final step is to retrieve the HTML rendering of our user control and insert it into the page. ASP.NET AJAX takes all of the hard work out of this step:
Sys.Application.add_init(AppInit); function AppInit() { RSSReader.GetRSSReader(OnSuccess, OnFailure); } function OnSuccess(result) { // Remove the .loading CSS from the div, to remove the // progress indicator background. Sys.UI.DomElement.removeCssClass($get('RSSBlock'), 'loading'); // Fill the div with the HTML generated from the user control. $get('RSSBlock').innerHTML = result; } function OnFailure() { // Do something if our callback fails. Retry it, perhaps. }
Since it’s only a CSS class, disabling the progress indication is as simple as using removeCssClass to remove it from the DIV. For more on removeCssClass, check out my recent article about ASP.NET AJAX’s client side UI methods.
With the animated background removed, we are now free to insert the rendered HTML into the DIV’s innerHTML. The end result is exactly the same as if we had placed the user control inside that DIV, without unnecessarily delaying the entire page load.
Conclusion
I think you’ll find that this technique is very powerful. It allows you to leverage your existing knowledge of ASP.NET and its server controls as a robust templating solution for lightweight AJAX. At the same time, it exudes the kind of professional usability that typically requires more tedious and less maintainable client side coding.
Note that there is not an UpdatePanel anywhere on this page. Using this technique does not require relying on partial postbacks. Not only does that improve performance, but also allows UpdatePanels elsewhere on the page to operate normally, while the deferred content loads.
Check out the full demonstration by downloading the source and running it yourself. You really have to see it in action to truly appreciate it. The download also includes at least one improvement that I didn’t have room to write about in this post. Give it a try and let me know what you think.
Similar posts
What do you think? Your comments are welcomed.
I appreciate all of your comments, questions, and other feedback, but please try to stay on topic. If you have a question unrelated to this post, I recommend posting on the ASP.NET forums. If you post there and then contact me with a link to the post, I'll try to take a look at it for you.
If you're replying to an existing comment, please use the threading feature. To do this, click the "Reply to this comment" link underneath the comment you're replying to.
Trackbacks
- rascunho » Blog Archive » links for 2008-02-05
- » Daily Bits - February 6, 2008 Alvin Ashcraft’s Daily Geek Bits: Daily links, development, gadgets and raising rugrats.
- links for 2008-02-07 « ThisIsSteve
- Chocolate for Dogs » Improving Page Responsiveness
- SitePoint Blogs » .NET on the ‘Net Feb 1-7: DataContext Dilemmas & Versioning Databases
- Using jQuery to Consume ASP.NET JSON Web ServicesEncosia @ ZDima.net
- Eye On .NET - Hisham Elbreky
- links for 2008-05-13 at James A. Arconati

Comments
Nice! I’ve been looking for something like this that doesn’t use UpdatePanels. Thanks!
This is very helpful. However I found that it doesnt work when my ascx file has an infragistics chart, panels and other controls.
Thanks for this — it’s a good technique. What is the request performance monitoring tool you are showing in the first screenshot?
It’s from FireBug’s “net” tab.
Now that is a nice little trick that I might just have to steal… I mean borrow. :-)
Thanks a lot for sharing this nice approach.
This isn’t really ASP.NET performance related. It just makes the pages load “asynchronously”. A poorly written deferred-loading method, can still cause the ASP.NET server to hang.
Right, but if you isolate it, it will no longer be able to cause your entire page to hang along with it.
At a technical level you haven’t improved performance, but none of your users actually realize or care about that technical differentiation. Perception is reality to them.
Yep, you are talking about two distinct issues, speed and responsiveness. This neat little trick will not improve the speed, but it will improve the responsiveness of the site. Therefore the users perceives a faster site, and that is what is important.
Great stuff! I had this same problem when loading slow banner ads, and ended up putting them in an iframe.
Good trick to speed up the initial page load, but you must remember that this keeps the “dynamic” content from being seen by the search engines. This is a good thing to do in an application, but not in a web site for the sake of SEO.
You’re right. That’s definitely a good point to keep in mind.
What of form post backs? I suppose this only works if the usercontrol does not have asp.net form controls that handle user interaction with server side events?
You’re correct.
On the surface, this is primarily aimed at read-only content. Definitely no postbacks. However, you could provide some interactivity without too much trouble.
Consider the case of an editable GridView. You could execute a web service to update its data from the client, and then call the user control rendering web service to display the updated grid.
In my experience, this approach is tremendously faster than using an UpdatePanel to switch to edit mode and then update and rebind.
While I think this was a good demonstration, I don’t think I would use it on a busy site. You would basically be speeding up the page for the first visitor, but slowing it down for each additional.
What I would do, is render the feed inline, but cache it in memory. You shouldn’t be hitting an RSS feed continually anyway. If it’s cached in memory, it’s going to be easier on your server just putting it as part of the page, instead of having to handle another request. You would also need to set a low timeout for the feed request, so that you don’t wait too long for it. Even better, if the feed isn’t available in the first 2 seconds of a request, have the page use your technique.
But, of course, in most real world situations, either solution will be perfectly fine.
You would definitely still want to make use of the cache in the web service. This and caching don’t need to be mutually exclusive.
For whatever reason, I noticed that subsequent requests using the demo code ended up being cached somewhere automatically. I actually had to add an artificial delay in order to test the UI code effectively.
I use LoadControl, then uc.InitializeAsUserControl(this); then uc.RenderControl(htw); where htw=new HtmlTextWriter(new StringWriter());
The result is in htw.InnerWriter.ToString();
By using “this” inside the page code you don’t have to instantiate another page. I don’t know which of the two ways is better though.
The difference here is that the code is executing inside of a web service, so “this” isn’t a Page instance in that scope.
Nice - I have a similar technique that I’ve presented on that I hope to write up for ASPAlliance.com soon. Some of the code is here, which may be useful to those of you going down this path. http://aspadvice.com/blogs/ssmith/archive/2007/10/19/Render-User-Control-as-String-Template.aspx
Great post (and good comment point about the SEO factor). My only thought was also regarding Scott Guthrie’s post at http://weblogs.asp.net/scottgu/archive/2006/10/22/Tip_2F00_Trick_3A00_-Cool-UI-Templating-Technique-to-use-with-ASP.NET-AJAX-for-non_2D00_UpdatePanel-scenarios.aspx which is improved upon by Steven Smith as mentioned above.
Those are good methods for statelessly rendering more complex controls in general, but I would be hesitant to pass data in through the presentation layer in this scenario.
What sort of situation would you see using those more complex methods in?
Great post! Thanks for the info - you combined the power of declaratively stating your UI code using a user control, with the flexibility and cacheability of web service, and made it look snazzy with a slick loading UI component.
Hi all,
there’s clearly a collection of asp experts following this thread. Do any of you have a pre-Ajax way of supporting this incremental loading? I’m trying to solve the problem for an ASP.Net 2.0 application. I’ve a strong suspicion that I may be able to leave some data-bound controls as un-bound, only binding them to their dataset after others have rendered to html. I believe this would meet the aspiration of this article without ajax.
I look forward to your feedback.
cheers,
Ian
You can do this in ASP.NET 2.0. The only thing here that’s 3.5 dependent is XLinq and the ListView.
You could use ADO/XPath and a Repeater in 2.0. If you can’t install ASP.NET AJAX, or don’t want to use it, you could use jQuery or Prototype to make the web service call.
This can be done with far less overhead if you utilize a HTTPHandler instead of a webservice/invoking the page class.
You can also do this without using the ASP.NET AJAX framework.
If you have example code that you feel is better, by all means, post it.
I’d be especially interested to see a method for rendering user controls to HTML in a web service (or HTTP handler), without using a page instance.
Hi Dave,
In this instance, you would not use a user control, instead you would implement a HTTP Handler that performs your LINQ to XML query, and then formats the HTML return based upon that. Considering the simplicity of what you are returning, a simple HtmlTextWriter and a foreach loop is all that is needed.
This would have far less overhead, and about the same amount of code (since your removing the ascx markup from the equation).
I don’t think I would want to prematurely optimize it, at the expense of maintainability. A year from now, I would much rather edit a user control template than HTTP handler code.
I have a feeling it wouldn’t matter much for this anyway, since the point is to defer slow content. Saving a few milliseconds while displaying a query that takes several seconds to run isn’t going to be very significant.
Though, it would be interesting to see a quantitative performance comparison between an HTTP handler rendering the control and a web service doing it.
Are you plan to write same .pdf that contains all your tips and tricks for asp.net performance?
Hi, Ivan. I don’t have any plans to do that at this time. I may in the future though (as quite a few people have asked for that).
I noticed that you use a javacript library
“jquery-1.2.2.min.js” can you explain what it is being used for?
Thanks in advanced.
That’s jQuery. It’s used for the fade-in effect you see in the downloaded source version.
I’ve tried this approach before. But it doesn’t work if you put a asp.net button or text box or any other form elements in your control. You’ll get error message saying that form elements must be inside the form tag.
What modification is needed if anybody want top 5 article from a rss feed.
You could add .take(5) to the LINQ expression to do that.
This approach works well on localhost. After loading the example to a remote site Firefox and Opera work as expected. However IE7 keeps failing with RSSReader undefined. Any suggestions?
If you’re running Fiddler in IE7, try disabling it.
Session state set to cookie autodetect doesn’t like it. Must be something like this http://forums.asp.net/t/1181395.aspx. Works better with the autodetect off. Thanks.
Interesting. Thanks for the followup comment.
A live demo would be nice
An idea I haven’t tried out yet, but maybe it would be a good idea to use a static instance variable for the Page object in the Web Service Class. My hope is that IIS server will put the thread to sleep for a while after a request and when another webservice request is made all static objects have persisted so there’s no need to construct the page object again, you can put a check to see if the static page is null then create a new instance. One important note though you have to remove the user control after it’s been built into the writer since the user control object will remain in the static page object.
I think you’d run into concurrency issues. What happens when two requests need to be rendered at the same time?
Hi
Inspired by the idea in the article, i did something like this
Page page = new Page();
UserControl ctl = (UserControl)page.LoadControl(”~/UserControls/SearchCriteria.ascx”);
page.Controls.Add(ctl);
StringWriter writer = new StringWriter();
HttpContext.Current.Server.Execute(page, writer, false);
return writer.ToString();
But, i am having problem with HttpContext.Current.Server.Execute(page, writer, false); . It give me an error “Error executing child request for handler ‘System.Web.UI.Page’.
” while every code in the user control is working fine.
Please help
thanx
Hi,
In my application, i try to load ascx file using
HttpContext.Current.Server.Execute
I use Icallbackeventhandler to load page asynchronously
but i get error…..
Error executing child request for handler ‘System.Web.UI.Page’.
When i make control read as normal html controls with runat=”server”…it works
for example
<asp:textbox> i change it to <input type=”text” runat=”server”> — this works,
but for controls like CalendarExtender i have the problem Error executing child request ,
is there a workaround for this? kindly let me know
My requirement is to load ascx page(having web controls), convert it to string and pass it to GetCallBackResult()
Thanks
This code and example doesn’t work.
The line above throws exeption ‘control must be placed inside a form tag with runat=server.’ which isn’t for the moment (we’re adding control into control tree of page rather than form)
I tried to create HtmlForm and add this into Page’s control tree, but still same.
Please let me know if anyone find solution.
Thanks
OK. I found solution for user controls requires server tag.
We need some post process after render control (to delete extra information of form)
here is the code
thanks.
Thanks for posting the solution that you found, Starianova.
Neo and Paul, does that method of executing an entire page work for what you need to do?
The javascript can be simplified by actually placing the loading progress div within the div you want the content to load in. Then, simply setting the innerHTML removes the loading layer automatically.
But what if you want to re-use the loading progress div?
The two divs are necessary to get the positioning and overflow how I wanted them.