Application State

ASP.NET offers two mechanisms for improving application performance by caching frequently used data: application state and the application cache. The former is a holdover from ASP. The latter is new to ASP.NET and largely obviates the need for application state. It also offers compelling features that application state does not. Application state might be useful for quickly porting old ASP code to ASP.NET, but new ASP.NET applications should use the application cache instead.

Application state and the application cache have three important characteristics in common:

When is it appropriate to use application state or the application cache? Imagine you’re writing a Web application that serves up real-time stock prices and that the application is backed by a massive database of stock prices that’s updated periodically. Rather than query the database every time someone asks for a stock price, you could query it every few minutes, cache the results in application state or the application cache, and retrieve prices directly from memory. Prices fetched from your site might be a few minutes old, but the decreased number of database accesses will enable the application to respond to individual requests much more quickly.

Using Application State

Application state is physically represented by instances of System.Web.HttpApplicationState. Pages access instances of HttpApplicationState through the Application property that they inherit from Page; Global.asax files access them through the Application property that they inherit from HttpApplication.

HttpApplicationState properties and methods expose the contents of application state. The following statements add three stock prices keyed by stock symbols to application state and work equally in an ASPX file, in Global.asax, or in a code-behind file:


You can also add items to application state using HttpApplicationState.Add:


However, Add and [] behave slightly differently when adding items to application state. Add adds an item even if an item with the specified key already exists; [] does not. These statements add two separate items to application state:


You can retrieve both items by iterating over the contents of application state, but if you simply ask for the item keyed by “AMZN,” you’ll get back 10. The following example, by contrast, adds just one item to application state. The first statement creates the item and assigns it the value 10. The second statement changes the item’s value to 11:


How do you read values from application state? The following statements retrieve the stock prices inserted earlier:


The casts are necessary to convert the generic System.Object references retrieved from application state to strong types. To remove items from application state, call Remove, RemoveAt, RemoveAll, or Clear through the Application property. Clear and RemoveAll are semantically equivalent; they empty application state by removing all items.

Locking and Unlocking

Internally, ASP.NET uses a reader/writer lock to synchronize access to application state so that two threads representing two concurrent requests can’t read it and write it at the same time. However, if you perform multistep operations on application state and need them to be treated as one atomic operation—that is, to ensure thread A can’t read from application state while thread B performs a multistep update—you should surround the updates with calls to Lock and UnLock, as shown here:


In between calls to Lock and UnLock, other threads that call Lock and UnLock can neither read nor write application state. Locking and unlocking is necessary only when multiple operations performed on application state must be treated as one.

The AppCounter Application

The application in Figure 9-4 uses application state to keep a running count of the number of times its pages are requested. Its one and only page is AppCounter.aspx, which reads the count from application state, increments it, writes the incremented count back to application state, and displays it. The Application_Start handler in Global.asax writes the count to application state each time the application starts up. The count is persistent—that is, it doesn’t reset to 0 when the application shuts down—because Global.asax includes an Application_End handler that saves the count in a text file named Count.txt when the application ends. Next time the application starts up, Application_Start reads the count from the file. Only if the file doesn’t exist or contains invalid data does Application_Start initialize the count to 0.

Application_Start calls Server.MapPath to convert Count.txt’s URL into a physical path name. Rather than do the same, Application_End reads the path from a static field (_path) initialized by Application_Start. The reason? Server.MapPath throws an exception if called from Application_End. By the time Application_End is called, the application has almost shut down and some HttpApplication facilities normally available to handlers in Global.asax are no longer usable. Server.MapPath happens to be one of those facilities.

Take the application for a spin by copying the two source code files to wwwroot (or the virtual directory of your choice) and calling up AppCounter.aspx in your browser. The resulting page should show a count of 1. Refresh the page a few times and observe that each refresh increments the count. Now close your browser, open a command prompt window, and type iisreset to restart IIS. This command shuts down the application, which causes Application_End to be called, which writes the count to Count.txt. Call up AppCounter.aspx again and the count should pick up right where it left off.

<%@?Import?NameSpace="System.IO" %>

<script?language="C#" runat="server">




Figure 9-4
AppCounter source code.
<%@?Page?Language="C#" %>


??????Response.Write?("Pages?in?this?application " +
????????? "have?been?requested " +?count?+ " time");