ne unfortunate by-product of the rapid move away from standalone and client/server applications to the Web-based environment is the disconnected nature of the HTTP protocol. Web servers and clients briefly exchange data, and then (by default) each forgets about the other entirely until the client makes another request. This meet-exchange-forget cycle particularly?affects the way that the individual pages of an application handle data?especially when there is a requirement to store data that is either expensive to create (in terms of resource and processing usage), or which is used regularly throughout the life of the application.
Since version 1.0, ASP has provided techniques to help you maintain state information and cache data for an application, as the Application and Session objects that it exposes. ASP.NET 1.0 added another headline feature, the application-level Cache object (complete with its support for output caching of ASP.NET-generated HTML), which is especially useful because it supports features to expire and invalidate items and pages stored in the cache. Now, ASP.NET version 2.0 adds new features that make using the Cache object and output caching an even more compelling solution.
This article contains an overview of the primary techniques available in ASP.NET for caching values and data between user requests, and then goes on to concentrate on the Cache object and output caching?with particular emphasis on the new features in version 2.0. You’ll see information about:
- Choosing the right approach for caching data.
- Application, Session and ViewState data caching.
- Global caching of data using disk files.
- The ASP.NET output caching features.
- Using the ASP.NET Cache object directly.
|Author’s Note: The ASP.NET version 2.0 code described in this article is based on the Beta 1 release of the .NET Framework v2.0. All other code is based on the final V1.1 release of the .NET Framework. You can download all the examples. If you install them on your own server, you may have to edit the connection strings in the web.config file, depending on the location of your database. There is a menu page named default.htm in each of the two folders in the samples (one for ASP.NET v1.1 and one for v2.0 Beta1).
Choosing a Caching Technology
The most important feature when choosing one of the available caching technologies is cache visibility. In other words, where do you need to be able to access the cached data? If it is just for the life of a page or over repeated postbacks from a particular page, you can take advantage of the ViewState of that page, which is basically a dictionary object that stores string values encoded into a hidden control on the page. Or you can use output caching to cache a portion or the entire HTML generated for a page so that the server does not have to re-execute the page for every request. In ASP.NET version 2.0, most of the new data source controls can cache their data, so you can also take advantage of that capability.
However, to store data required to be accessible across more than one page, you need an alternative technique. If you only need to make the values available for a specific user, you can use the Session object to store values and data. As long as the user’s browser supports cookies, or you turn on cookieless session support, data in the user’s Session store will be available from any page they open while the session is active.
If you want to make your data visible to all the users of an application, you must use either the ASP.NET Application object or the Cache object. The advantage of the Cache object is that you can program expiration rules and manage the lifetime of the data more closely. Of course, you can use the Application and Cache objects for user-specific data as long as you manage the identification of each instance of the data, probably through unique or user-specific cache key names.
Finally, suppose you want to make cached data available to all users and all applications on a server, or even on a networked group of servers? The only real answer here is to use some sort of disk or memory persistence implemented and managed outside of ASP.NET. In fact this is reasonably easy to do, using the classes implemented in the .NET Framework class library for reading and writing streams (including memory streams) and disk files. In general, disk-based storage is the better option for longer persistence periods, because it can survive server restarts, application failures, and most other events that would cause other forms of cached data to be lost.
Table 1 shows the features of each technique briefly mentioned here, including a note about the volume of data that each is designed to cope with. For example, ASP.NET transmits the ViewState of a page over the network twice with each page request, and so you should keep it to a minimum. If you have a great many concurrent users, even small volumes of data stored in each user’s Session can swallow up valuable memory on the server unless you configure alternative Session support features such as the ASP.NET State Store or storage in a central SQL Server database.
Table 1. Cache Visibility: The table lists data visibility and optimum size for various caching techniques.
|Optimum data size
|Data source controls
|Page output caching
It’s important to note that you don’t have to choose the most restrictive visibility for your cached data. For example, disk-based storage?perhaps as an XML file?would be an ideal solution for data that is expensive to retrieve from its source and yet changes infrequently (particularly if accessed only occasionally by users). But what if each user requires a different set of data? You could still cache it using some user-specific value in the filename, perhaps the Session ID or user login name. The only downside is that you have to manage refreshing and removing stale data yourself.
The ASP.NET Application, Session and ViewState
You use the Application and Session objects in ASP.NET in essentially the same way as in “classic” ASP, except that you should be aware of data typing issues. It’s always a good idea to cast or convert values to the correct type when you extract them. For example:
Dim iThisValue As Integer = 42 Application("MyInteger") = iThisValue Dim iThatValue As Integer = CType( _ Application("MyInteger"), Integer)
In C#, that same code is:
int iThisValue = 42; Application["MyInteger"] = iThisValue; int iThatValue = (int) Application["MyInteger"];
Alternatively, you can use the methods exposed by the HttpApplicationState class (from the System.Web namespace), which is the class used to implement the Application object in ASP.NET. These methods include Add, Clear, Get, Remove, RemoveAt, and Set, plus properties and methods to retrieve information about the keys for the stored items such as AllKeys, Count, GetKey, etc.
|Author’s Note: See the .NET Framework SDK topic “HttpApplicationState Members” in the “Reference | Class Library” section for more details.
ASP.NET itself uses thread locking to automatically lock the Application (and Session) to prevent concurrent updates to the values stored there from causing inconsistent results; however if you are updating values in the Application you should use the Lock and Unlock methods as in previous versions of ASP:
Application.Lock() Dim iThisValue As Integer = CType( _ Application("MyInteger"), Integer) iThisValue += iMyIncrement Application("MyInteger") = iThisValue Application(UnLock)
You aren’t limited to storing only simple intrinsic .NET value types in the Application (or Session). You can store any serializable class instance, for example a DataSet (in version 2.0, the DataTable is also serializable). As an example, this code stores a DataSet in the Application:
Application("MyDataSet") = dsMyData Dim dsRetrievedData As DataSet = _ CType(Application("MyDataSet"), DataSet)
The HttpSessionState class (in the System.Web.SessionState namespace) implements ASP.NET’s Session object. The Session object has no Lock or Unlock method, though you can use the IsSynchronized and SyncRoot properties to create your own thread-safe implementations if you wish. However, for general use, the Session works just like the Application for reading and storing values.
Remember that the Session exposes some useful properties such as IsCookieless and IsNewSession, which you may find useful. But the most important feature in ASP.NET in terms of improving page execution efficiency is the implementation of read-only sessions. ASP.NET raises a whole series of events as it loads and executes an ASP.NET page, and two of these are concerned with loading and saving Session data. As a page loads, an event causes ASP.NET to search the Session for any values that apply to the page, and load those it finds into the execution memory space for the page. As execution of the page ends, another event causes ASP.NET to write those values back to the Session store.
So it’s pretty obvious that you can reduce page execution overheads by avoiding the two event calls if you don’t need to read or update session data. And if you only need to read it, but not update it, you can specify read-only access to the Session. To specify that a page does not require access to the user’s session, add this directive:
To allow your application to read session data but not update it, thus avoiding the expensive process of updating the session data at the end of the page, use this directive:
Note that there is also a property named IsReadOnly on the HttpSessionState class that you can query in your page code to see if the current session is read-only. You can also specify the session behavior at machine or application level using the
If you need to cache only a single value or a small set of data for an individual page between postbacks, you can use the ASP.NET ViewState. When an ASP.NET page contains a server-side HTML form (a