Application, Session, HttpContext, and ViewState Caching






Application, Session, HttpContext, and ViewState Caching

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 State

The 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.

Storing Data in the Application

DataTable cachedData = (DataTable)Application["CommonData"];
if (cachedData == null)
{
  cachedData = DataLayer.FetchCommonData();
  Application["CommonData"] = cachedData;
}

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 State

Session 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.

Storing Data in the Session

DataTable cachedData = (DataTable)Session["UserData"];
if (cachedData == null)
{
  cachedData = DataLayer.FetchUserData();
  Session["UserData"] = cachedData;
}

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 State

Disabling 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 State

At the application level, you configure Session state in web.config, as seen in Listing 6.3.

The attributes are documented in Figure.

Attributes of SessionState Configuration

Attribute

Description

allowCustomSqlDatabase

Only relevant when mode is set to SQLServer, and indicates whether or not a custom database name can be specified in the Initial Catalog attribute of the SQL Server connection string. The default value is false, meaning the default ASP.NET session database is used.

cookieless

Indicates how cookies are used, and can be one of:

AutoDetect, where ASP.NET determines whether the requesting device supports cookies. If so, then cookies are used; otherwise the query string is changed to include the session identifier.

UseCookies, where cookies are always used. This is the default value.

UseDeviceProfile, where ASP.NET uses the browser capabilities to determine whether cookies should be used.

UseUri, where the query string is always used.

true, which has the same effect as UseUri.

false, which has the same effect as UseCookies.

cookieName

Defines the default cookie name used to store the session ID. The default value is ASP.NET_SessionId.

customProvider

Indicates the name of the provider when the mode is Custom. The name attribute should match one of the provider names declared in the <Providers/> section, and defaults to an empty string.

mode

Indicates how session state is being managed, and can be one of:

Custom, which indicates that session state is stored in a custom manner.

InProc, where session state is stored within the ASP.NET process. This is the default value.

Off, where session state is turned off for the application.

SQLServer, where session state is stored in a SQL Server database.

StateServer, where session state is stored in a separate ASP.NET State Service.

partitionResolverType

Defines a type to be used to resolve the connection string for the request. Resolvers are used to enable session state to be partitioned to scale in Web Farm situations. If this attribute is set, the sqlConnectionString and stateConnectionString attributes are ignored. The default value is an empty string.

regenerateExpiredSessionId

Indicates whether or not the session identifier will be reissued when an invalid identifier is used by the client. The default value is true, where identifiers are only reissued when cookies aren't being used.

sqlCommandTimeout

Indicates, in seconds, the timeout for a SQL command when the mode is SQLServer. The default value is 30.

sqlConnectionString

Indicates the name of the connection string when using SQL Server to store session state. The default value is "data source=127.0.0.1; Integrated Security=SSPI", pointing at a local, trusted SQL Server database.

stateConnectionString

Required when mode is StateServer, and defines the server name/address and port where session state is stored. The default value is "127.0.0.1:42424".

stateNetworkTimeout

For when mode is StateServer, and defines the number of seconds to wait for the remote state server before the request is cancelled. The default value is 10 seconds.

timeout

Defines the number of minutes to wait after session activity (i.e., idle time) before the session is abandoned. The default value is 20 minutes.

useHostingIdentity

Indicates whether or not session state will revert to the hosting identity or use client impersonation. The default value is true, indicating that the identity of the hosting process (ASPNET on IIS5 or NETWORK SERVER on IIS6) or the identity specified in the process <identity> section is used. If false, the credentials of the current OS thread are used.


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.

Session State Configuration

<sessionState
  allowCustomSqlDatabase="[true|false]"
  cookieless="[AutoDetect|UseCookies|UseDeviceProfile|
               UseUri|true|false]"
  cookieName="String"
  customProvider="String"
  mode="[Custom|InProc|Off|StateServer|SQLServer|]"
  partitionResolverType="String"
  regenerateExpiredSessionId="[true|false]"
  sessionIdManagerType="String"
  sqlCommandTimeout="Integer"
  sqlConnectionString="String"
  stateConnectionString="String"
  stateNetworkTimeout="Integer"
  timeout="Integer"
  useHostingIdentity="[true|false]"
  >
  <providers>
    <clear />
    <add
      Name="String"
      Type="String"
      [providerSpecificConfiguration] />
  </providers>
</sessionState>

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 HttpContext

If 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:

Simple Binding to a Business Layer

protected void Page_Load(object sender, EventArgs e)
{
  if (!Page.IsPostBack)
  {
    GridView3.DataSource = Shippers.GetShippers();
    GridView3.DataBind();
  }
}

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.

A Caching Class Using the HttpContext

public static class Caching
{
  public static List<Shipper> GetShippers()
  {
    List<Shipper> ships =
        (List<Shipper>)HttpContext.Current.Items["Shippers"];
    if (ships == null)
    {
      ships = Shippers.GetItems();
      HttpContext.Current.Items["Shippers"] = ships;
    }
    return ships;
  }
}

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 ViewState

Another 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.



 Python   SQL   Java   php   Perl 
 game development   web development   internet   *nix   graphics   hardware 
 telecommunications   C++ 
 Flash   Active Directory   Windows