Using the Application, Session, HttpContext and ViewState objects for caching data is not a new technique, and while extremely simple, it shouldn't be left out or ignored because of other techniques. All three objects provide simple key based collections for storage of data through the lifetime of the object. Since this lifetime is not persistent, you should only store data that is ephemeral in nature; anything that requires long-lived storage should use a database, or perhaps the Profile object for user-related data. Using the Application StateThe Application object exists for the lifetime of the application; that is, from the moment the first request to the application is received to the moment the application is shut down. Application shutdown can occur under different circumstances, and you should be aware that it can happen while the site is being used. ASP.NET is self-monitoring and can restart an application if, for example, memory demands exceed set limits. This means that you shouldn't rely on an item automatically being stored in the Application object; you should always check for a null value. Using the Application object for state storage is as simple as indexing the Application object. For example: Application["Start"] = DateTime.Now; This will add the current date and time to the Application cache, indexed by Start. To extract the value, you use the same indexing scheme; but the application stores objects, so casting is required: DateTime appStart = (DateTime)Application["Start"]; Because object storage is supported, you can store complex types, such as data. For example, a common caching pattern is to check to see if the data is in the cache (irrespective of which form of caching is used), and if it's present, return the data. If the data isn't present in the cache, it is fetched from its original location and stored in the cache. For example, consider some data from a database that is required in all pages, which could be stored in the application, as shown in Listing 6.1. Listing 6.1. Storing Data in the Application
Here the data is fetched from the Application, which returns null if the item isn't present, and if it isn't present, then it is fetched from the data layer and placed in the Application for subsequent requests. When using this form of caching, you have to balance the resource use (when storing the data in the application) against the time taken to fetch it from its original location. Performance and memory monitoring tools are useful in helping you make this decision. If you know that every single page is going to use some cached data, you can use the Application_Start event to load the data, because this event runs once when the application starts. In this situation, you wouldn't need to check for the cached item, because you know it wouldn't be present when the application is just starting. If, however, only a selected number of pages use the cached data, or if use of the cached data is dependent upon user actions, you can use the code in Listing 6.1 to lazy load the datathat is, load it only when it is first requested and then cache it for later use. Using the Session StateSession state is similar in use to Application state, but with one major exception: It is unique to each user of the site and is destroyed when the user leaves the site (after a timeout). Session state is therefore useful for storing data that a user would require throughout his or her use of the application. Bear in mind that Session state is intended for storage of transient datadata that doesn't need to be retained after the user leaves the site. For long-lived data, such as user preferences, you should use the Profile. Listing 6.2 shows a common pattern for using the Session object for state storage. Listing 6.2. Storing Data in the Session
Like the Application state, Session state takes resources, so you should examine your needs carefully. By default, Session state is enabled for applications and pages but can be turned off or disabled completely. Disabling Session StateDisabling Session state is a performance optimization that you can perform at several levels. In pages, you can use the EnableSessionState attribute of the Page directive: <% Page EnableSessionState="false" ... %> Alternatively, if you require access to Session state but don't plan to update it, you can make it read-only for a page: <% Page EnableSessionState="ReadOnly" ... %> This ensures that you still have access, but don't go through the overhead of locking the state for update. Configuring Session StateAt the application level, you configure Session state in web.config, as seen in Listing 6.3. The attributes are documented in Table 6.1.
You can see that there are a number of ways in which Session state can be stored. By default, the ASP.NET process stores the state, because this provides the fastest storage. However, because it is process-bound, Session state would not survive an application restart, which is where the state server and database options come in. The downsides of these, however, are that performance is slower than with the in-process method. For more detailed information on session state and performance, there is an excellent article in the MSDN Magazine, available online at http://msdn.microsoft.com/msdnmag/issues/05/09/SessionState/default.aspx. Listing 6.3. Session State Configuration
The SessionState configuration element should not be confused with the SessionPageState element, which is used to keep a history of view state and control state within the session. Using HttpContextIf you don't need to store data across an entire session, but perhaps require data across multiple user controls within a page, then you can use the current context of the request. Each request has an associated HttpContext object associated with it, which provides access to many objects used within pages, such as the Request, Profile, and Trace. Also available on the context is an Items collection that can be used for storage and is particularly useful when you have multiple user controls on a page that need to share data. It is important to realize that this technique is only useful between controls within a single end-to-end request and that it does not apply between separate page requests. For example, consider two grids that use the same data. You could use the data source controls and their built-in caching, but if you have an existing code library and need to bind in code, you might have the code shown in Listing 6.4 in both user controls: Listing 6.4. Simple Binding to a Business Layer
This code, if used in multiple user controls, would result in the same SQL command being run multiple times. There are several ways to cure this, and we'll look at others later in the chapter, but a simple solution would be for one control to read the data and cache it in the context. Rather than explicitly putting the code into your user control (which would limit the order of the controls on the page to ensure that the one that cached the data was executed first), you could create a central class, as shown in Listing 6.5. Listing 6.5. A Caching Class Using the HttpContext
This code is extremely simple and follows the by-now familiar pattern used in caching. It first fetches the data from the Items collection, and if it's not present in the cache, gets the data from the Shippers business class and stores it in the Items collection. Subsequent calls will fetch it from the collection. Using ViewStateAnother method of caching data is to use ViewState, although this does come with the warning that ViewState is transferred to and from the client on each request. The ViewState can be accessed just like other collections: ViewState["CachedData"] = DateTime.Now; You should generally try to use as little ViewState caching as possible in order to reduce overheads in transferring pages, but it does provide an alternative storage mechanism for small amounts of data. For best performance, you should turn off ViewState for controls and pages that don't require it. ASP.NET 2.0 supports a new feature for state storage, Control-State, which controls use to support the minimum state requirements for the control to operate. This allows ViewState to be turned off but for the control to still operate correctly. |