Speed Up Your ASP.NET Pages
Technology Toolbox: ASP.NET
Two and a half years have passed since Microsoft first released ASP.NET. Yet even after that much time and experience with ASP.NET, developers are still asking about performance. The issue comes up constantly in discussion groups and during Q&A time at user groups.
These seven performance tips will help speed up your ASP.NET-based pages and sites. I'm assuming you're being careful with your HTML coding, of course. Otherwise, classic ASP, Java, and even ASP.NET can run poorly.
Users will still think your site is slow if their connection is slow or your Web server is overloaded. Of course, this doesn't let you off the hook. All else being equal, efficient ASP.NET coding makes a difference in what users see as the performance of your Web site. See if these tips don't do the trick for you.
1) Use Page.IsPostBack
You need to take a series of steps when your page loads for the first time to perform setup formatting on the page or initialize data. However, you don't need to keep reinventing this wheel the next time a page is requestedand the next and the next. Avoid initializing the data again on the postback. For example, let's say a Web page pulls data from a Web service concerning information about a news header for the page:
if( !Page.IsPostBack )
// Get the class data
dbAccess oWS = new dbAccess();
DataSet ds = oWS.GetNewsInfoDS();
This data is static, so it needs to load only on the first execution of the page. You can ensure it doesn't occur more often by using the IsPostBack property of the Page class. You can also cause some elusive page bugs by not using IsPostBack. For instance, your breakpoints might show the expected data in the controls on a page after the user clicks on a button, but when the page is reposted, the data gets reset to the values the controls had before the page was reposted. This glitch stems from the sequence of events that occurs when a page is being posted back to the server.
2) Use SQL Server Stored Procedures for Data Access
SQL Server stored procedures can improve performance for both Windows applications and ASP.NET Web applications. You boost performance when you write and save a command as a stored procedure. SQL Server optimizes that command and stores it in a compiled state, making queries run faster than when you submit them as ad hoc SQL statements. The SQL managed provider also talks to SQL Server in its own language, which shrinks the front-to-back time required to get a query through and data back to your client.
Stored procedures provide data security benefits as well. Hackers can type certain characters into the username and password boxes of your standard login script and magically gain access to your site's database. Fortunately, stored procedures help resist script attacks. It's good practice to forbid queries by anything other than stored procedures, which helps guard your valuable or sensitive data.
3) Utilize the HttpServerUtility.Transfer method
The .NET Framework's vastness might obscure access to classes or methods that improve upon familiar techniques. For example, you probably use the Server object's Request method when you want to redirect page execution from the running page to another page. But you'll do yourself a favor if you switch to the HttpServerUtility class's Transfer method for redirection within the content of the current site. Using this method eliminates unnecessary client/server round trips and improves site performance appreciably. Even the fastest execution on the server amounts to spinning your wheels if you're constantly pushing data over the wire at the same time.
This wheelspinning happens all too frequently with data validation, where overzealous use of server-side validation produces constant communication with the client. You're better off performing validation in script on the client when you can, rather than passing the work back to the server. Obviously, you must return to the server to validate some things, but do work on the client when you can and save the round trip.
4) Don't Overdo a Good Thing
Save View State Only When Necessary.
Server-side controls save view state by default. Inspect your Web pages' source code and you'll see why this behavior can hamper performance. Textboxes and other simple controls don't impose much overhead in the view state. More complex controls are another matter. For example, a data grid might send a 10K block of Base-64-encoded text back and forth between the client and server (see Figure 1). That's not a problem if it's going over a high-speed network, but users connected over a 56K modem might think your Web site is a real slug while this invisible conversation is going on.
Don't Rely on Exceptions.
Exceptions are good and useful, but they're costly. Every exception raised in a Windows application produces a noticeable delay in execution. Don't make the common mistake of using exceptions to pass information from one routine to the next with queries such as, "Did the method succeed or fail?" Nor should you signify that a message needs to be reported in a dialog. Use language constructs such as if/else statements if you want to alter the execution flow of your application. The overhead required to unwind a call stack and perform an exception notification vastly exceeds the overhead needed to make a change in the program's execution path.
Your exception-based code can impact the server as wellthe same server that might be busy running requests from other sites and that is most likely being pushed a lot harder than a desktop system. Your application's exception-based delays on the server might not be as obvious as those on a Windows app, but they still contribute to users' perception of the performance of your application on their desktops.
Restrict Use of Session State.
Session state is another of the .NET Framework's great built-in features, but turn it off wherever you can. This feature is turned on by defaultand like exceptions, it isn't free. You pay a maintenance overhead and subsequent performance cost for supporting a page's session state. Avoid this cost either by using page directives to disable the state for a page completely or by making the pages' access to session state read-only:
<%@ Page EnableSessionState="false" %>
<%@ Page EnableSessionState="readonly" %>
Disable session state completely through the site's configuration file when your entire site doesn't need it:
<sessionState mode="off" />.
It gets more complicated when you do need session state in some places. This snippet of a configuration file shows how you can store it as in-process session state:
You can also store session state as out-of-process session state, either as a Windows service or in a SQL Server database. You'll get the most application speed by storing it in-process. On the other hand, out-of-process options work best sometimes. Have fun choosing the best set of trade-offs for your situation.
5) Limit ASP.NET Server Controls
Exceptions and session state impose overhead. Surpriseso do ASP.NET server controls. They require server-side support, which leads to communication between your server and the client to support the control, and resource consumption on the server. Avoid the temptation to drop the server control onto your form and forget about it. In fact, you might be better off with a regular HTML control. Use the server control only if you plan to control the properties of a control programmatically, use view state to hold data values of the control, or handle server-side events generated by the control.
6) Precompile Your Apps
Every time you change a page on your Web site (and its containing assembly), you need to recompile that page when a user first touches it. We've all noticed this during development: You make changes, press F5, and wait until the page is compiled and loaded. There is considerable discussion about whether this is really a problem that requires much attention.
If you have a large Web application, you have several approaches to performing pre-batch compilation to get the entire site compiled and ready to run. First, there are several utilities available that will walk the pages of your site and do an HTTP request for each page. This forces ASP.NET to load and compile each page. This "automated" process might not work if a page requires authentication. Second, you can browse the site manually after you upload it to the server. The drawback is that it can be difficult to "touch" every page if your site has hundreds of pages. If you can't touch every page, you could visit the main pages of the site that give a user the first impression of your site. A slow first page is a poor introduction, but a slow-loading page later in the process is not such a turnoff.
First impressions are critical because unless you have customer loyalty, today's users will go somewhere else rather than wait for a poor-performing site. The question about precompiling rests on the premise that only the first user of thousands of users will see a delay in loading. Is it worth the extra work and concern for such little return? If you're concerned, Microsoft has the solution for you in the next version of .NET. Whidbey will have a precompiler ASP handler you can use to get the most performance from your site by reducing the compile time as a user loads each page.
7) Use ASP.NET Caching
Cache the server-based data that's used repeatedly and doesn't change between each request from a page. You must strike a balance between what data to cache, when, and for how long. Don't cache too many different items. You pay an overhead penalty for maintaining the cache, and you pay an extra penalty as the system keeps the cache up to date tracking all those bits and pieces (see Figure 2).
Don't put items in the cache that expire quickly. The maintenance overhead might negate the benefit of caching if ASP.NET must update the cache with fresh data every second or two. Unfortunately, no hard and fast rule says, "If the item in the cache updates less than every X seconds, don't put it in the cache." You can implement caching on your ASP.NET pages easily by using either page directives or Response object methods.
Placing this code in your ASPX page tells ASP.NET to use a page directive to cache page output for 60 seconds:
<%@ OutputCache Duration="60"
The VaryByParam attribute causes a conditional update of the cache. Subsequent page requests are fulfilled using the cached information. ASP.NET rebuilds the page and updates the cache after the specified time. You can accomplish the same action with this method call in your page code:
Perhaps your site is an online catalog with several categories of products. Users browse through the categories, looking at products. The pages include a list of productseach with a picturein each product line, and you've cached different pages for different product types. You know most customers will request a look at the section dealing with your most popular product line, for example, and you want to cache versions of the page for these products and for memory. Add this directive to your page:
<%@ OutputCache Duration="60"
ASP.NET caches your mobility page when customers make this request:
A request for the memory product line caches a second page along with the mobility product page. Future requests use that cached page for the next 60 seconds. The caching is flexible enough to let you cache on multiple values:
<%@ OutputCache Duration="60"
If your product page were to vary by customer ZIP code, the directive would create different pages based on both criteria.
You can see from this example the extensive, flexible control you have over page caching. You have many opportunities for enhancing site performance. Caching, in particular, improves the performance of most Web sitesas long as you use it judiciously.
About the Author
Dan Fergus is the chief architect at Forest Software Group, developing .NET applications, including Pocket PC sports team applications. He speaks at major conferences, does consulting, and teaches Compact Framework, VB.NET, and ASP.NET courses. He coauthored The Definitive Guide to the .NET Compact Framework (Apress). Reach him at [email protected].