Place the code needed to find
and
load the data in the Application_Start
method of
global.asax
and store it in the
Application
object.
In the code-behind class for global.asax
, use
the .NET language of your choice to:
Create an event handler for the Application_Start
event.
Load the application data and store it in the
Application
object.
The code we’ve written to demonstrate this solution
is shown in Example 6-1 through Example 6-5. Example 6-1 and
Example 6-2 show the VB and C# code-behind files for
global.asax
; this code reads data from a
database and places it in the Application
object.
Figure 6-1 shows a simple form that
we’ve created to view the application state data.
Example 6-3 shows
the .aspx
file that produces the form. Examples
Example 6-4 and Example 6-5 show
the companion VB and C# code-behind files that demonstrate how to
access the application state data.
The purpose of the Application
object
is
to store information once that can be simultaneously shared with all
users of the application without having to access it repeatedly from
a database or some other data store. A simple example is when you
want to store and then share the number of times an application has
been hit by all the users of the application. Another example is when
you want to store and then share some common reference information,
as illustrated in Figure 6-1. Using the
Application
object provides the ideal means to
accomplish these tasks. The Application
object is
similar to the Session
object (discussed in the
next recipe), except that it stores global information as opposed to
information about an individual session. The
Application
object is a property of the
Page
object that provides the ability to store
virtually any data and offer access to it throughout the application
by all users. Storing commonly used global data in memory can
significantly improve the performance of an application.
The code we’ve written to illustrate this recipe
provides a simple example of loading information from a database and
storing it in the Application
object so it can be
accessed by any user on any page of an application without having to
again retrieve the data from the database each time the data is
needed.
Data that you want to make available throughout the application
should be initialized when the application is started. The
Application_Start
event handler in the
global.asax.vb
(or
global.asax.cs
) class is the best place to do
this, because the Application_Start
event is
raised the first time the application is accessed.
In our example, the Application_Start
event
handler reads the titles of the chapters of this book from a database
into a DataSet
. We then store the table of chapter
data as a smaller DataTable
object in the
Application
object.
Because the chapter data stored in the Application
object will be used for read-only data binding, the overhead of a
DataSet
object is not required. Therefore, a
smaller DataTable
object, which represents one
table in a DataSet
, is stored in the
Application
object instead.
Data stored in the Application
object can be
accessed from any page of an application. When you access the data,
you must cast it to the correct type because all data is stored in
the Application
object as
Objects
; without properly casting the data, your
code will not compile. Our example retrieves the data and binds it to
a Repeater
control in the
Page_Load
method in Example 6-4 and Example 6-5. (Data
binding is described in Chapter 1.)
To avoid “hardcoding” names of
variables placed in the
Application
object, we recommend that you instead
define public constants in the global class, as shown in Example 6-1 (VB) and Example 6-2 (C#).
Any object placed in the Application
object must
be free-threaded or else deadlocks, race conditions, and access
violations can occur. Any object created from the classes in the
Common Language Runtime is free-threaded and can be safely stored in
the Application
object. VB6 objects are
apartment-threaded and should not be placed in the
Application
object.
You can update data stored in the Application
object whenever your application requires it. ASP.NET is
multithreaded, so many threads can access the variables within the
scope of an application at the same time. Changing the
variables’ values in an uncontrolled fashion can
cause data concurrency issues. To prevent contention between threads,
the Application
object must be able to block
access to itself while changes are made. You can do this by calling
the Lock
method of the
Application
object, making your changes, and then
calling the Unlock
method, as shown here:
Application.Lock( )
Application.Add(APP_CHAPTER_DATA, _ ds.Tables(CHAPTER_TABLE))Application.UnLock( )
Application.Lock( );
Application.Add(APP_CHAPTER_DATA, ds.Tables[CHAPTER_TABLE]);Application.UnLock( );
Your application should always minimize the time the
Application
object is locked, because all other
threads are held off until the lock is released. To avoid permanent
locks, ASP.NET automatically performs the unlock operation when a
request is completed, a request times out, or an unhandled exception
occurs and causes the request to fail.
Whenever you use the Application
object in an
application, be sure to consider the following points:
The Application
object is not shared across
servers in a web farm. Each server maintains its own copy of the
Application
object. If the values stored in the
application are different on each of the servers in the web farm,
your application may operate differently depending on which server
was accessed.
Any data stored in
the Application
object is cleared when the
application restarts. If any of that data needs to be persisted, you
can place code in the Application_End
event
handler of global.aspx
to store the data in a
database or the like. Be careful in relying on the
Application_End
event handler to persist any
important data. It is not called if the application stops abnormally,
such as in a power failure or an application crash.
Example 6-1. Maintaining application state (global.asax.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: Global.asax.vb ' ' Description: This module provides the code behind for the ' Global.asax page ' '***************************************************************************** Imports Microsoft.VisualBasic Imports System Imports System.Configuration Imports System.Data Imports System.Data.OleDb Namespace ASPNetCookbook.VBExamples Public Class Global Inherits System.Web.HttpApplication'the following constant used to define the name of the variable used to
'store the chapter data in the application object
Public Const APP_CHAPTER_DATA As String = "ChapterData"
'************************************************************************* ' ' ROUTINE: Application_Start ' ' DESCRIPTION: This routine provides the event handler for the ' application start event. It is responsible for ' initializing application variables. '-------------------------------------------------------------------------Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs)
Const CHAPTER_TABLE As String = "Chapter"
Dim dbConn As OleDbConnection
Dim da As OleDbDataAdapter
Dim ds As DataSet
Dim strConnection As String
Dim strSQL As String
Try
'get the connection string from web.config and open a connection
'to the database
strConnection = _
ConfigurationSettings.AppSettings("dbConnectionString")
dbConn = New OleDbConnection(strConnection)
dbConn.Open( )
'build the query string and get the data from the database
strSQL = "SELECT ChapterNumber, Description " & _
"FROM Chapter " & _
"ORDER BY ChapterNumber"
'fill the dataset with the chapter data
da = New OleDbDataAdapter(strSQL, dbConn)
ds = New DataSet
da.Fill(ds, CHAPTER_TABLE)
'store the table containing the chapter data in the Application object
Application.Add(APP_CHAPTER_DATA, _
ds.Tables(CHAPTER_TABLE))
Finally
If (Not IsNothing(dbConn)) Then
dbConn.Close( )
End If
End Try
End Sub 'Application_Start
End Class 'Global End Namespace
Example 6-2. Maintaining application state (global.asax.cs)
//---------------------------------------------------------------------------- // // Module Name: Global.asax.cs // // Description: This module provides the code behind for the // Global.asax page // //**************************************************************************** using System; using System.Configuration; using System.Data; using System.Data.OleDb; namespace ASPNetCookbook.CSExamples { public class Global : System.Web.HttpApplication {// the following constant used to define the name of the variable used to
// store the chapter data in the application object
public const String APP_CHAPTER_DATA = "ChapterData";
//************************************************************************ // // ROUTINE: Application_Start // // DESCRIPTION: This routine provides the event handler for the // application start event. It is responsible for // initializing application variables. //------------------------------------------------------------------------protected void Application_Start(Object sender, EventArgs e)
{
const String CHAPTER_TABLE = "Chapter";
OleDbConnection dbConn = null;
OleDbDataAdapter da = null;
DataSet ds = null;
String strConnection = null;
String strSQL = null;
try
{
// get the connection string from web.config and open a connection
// to the database
strConnection =
ConfigurationSettings.AppSettings["dbConnectionString"];
dbConn = new OleDbConnection(strConnection);
dbConn.Open( );
// build the query string and get the data from the database
strSQL = "SELECT ChapterNumber, Description " +
"FROM Chapter " +
"ORDER BY ChapterNumber";
// fill the dataset with the chapter data
da = new OleDbDataAdapter(strSQL, dbConn);
ds = new DataSet( );
da.Fill(ds, CHAPTER_TABLE);
// store the table containing the chapter data in the Application object
Application.Add(APP_CHAPTER_DATA,
ds.Tables[CHAPTER_TABLE]);
} // try
finally
{
// cleanup
if (dbConn != null)
{
dbConn.Close( );
}
} // finally
} // Application_Start
} }
Example 6-3. Using application state data (.aspx)
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH06ApplicationStateVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH06ApplicationStateVB" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Application State</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmApplicationState" method="post" runat="server"> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center" class="PageHeading"> Maintaining Application State (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center"> <table width="500" border="0" align="center" cellpadding="0" cellspacing="0"> <thead> <tr> <th colspan="2" class="PageHeading">Chapters in the ASP.NET Cookbook</th> </tr> </thead> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <asp:Repeater id=repMenuItems runat="server"> <ItemTemplate> <tr class="MenuItem"> <td align="center" width="50"> <%# DataBinder.Eval(Container.DataItem, _ "ChapterNumber") %></td> <td width="450"> <%# DataBinder.Eval(Container.DataItem, _ "Description") %></td> </tr> <tr height="2"> <td bgcolor="#FFFFFF" colspan="2"> <img src="images/spacer.gif" border="0"></td> </tr> </ItemTemplate> </asp:Repeater> </table> </td> </tr> </table> </form> </body> </html>
Example 6-4. Using application state data code-behind (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH06ApplicationStateVB.aspx.vb ' ' Description: This module provides the code behind for the ' CH06ApplicationStateVB.aspx page ' '***************************************************************************** Imports Microsoft.VisualBasic Imports System Imports System.Data Namespace ASPNetCookbook.VBExamples Public Class CH06ApplicationStateVB Inherits System.Web.UI.Page 'controls on the form Protected repMenuItems As System.Web.UI.WebControls.Repeater '************************************************************************* ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '-------------------------------------------------------------------------Private Sub Page_Load(ByVal sender As System.Object, _
ByVal e As System.EventArgs) Handles MyBase.Load
Dim chapterData As DataTable
If (Not Page.IsPostBack( )) Then
'get the chapter data stored in the application object
chapterData = CType(Application.Item(Global.APP_CHAPTER_DATA), _
DataTable)
'bind it to the repeater to display the chapter data
repMenuItems.DataSource = chapterData
repMenuItems.DataBind( )
End If
End Sub 'Page_Load
End Class 'CH06ApplicationStateVB End Namespace
Example 6-5. Using application state data code-behind (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH06ApplicationStateCS.ascx.cs // // Description: This module provides the code behind for // CH06ApplicationStateCS.ascx // //**************************************************************************** using System; using System.Data; using System.Web.UI.WebControls; namespace ASPNetCookbook.CSExamples { public class CH06ApplicationStateCS : System.Web.UI.Page { // controls on the form protected System.Web.UI.WebControls.Repeater repMenuItems; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------private void Page_Load(object sender, System.EventArgs e)
{
DataTable chapterData = null;
if (!Page.IsPostBack)
{
// get the chapter data stored in the application object
chapterData = (DataTable)(Application[Global.APP_CHAPTER_DATA]);
// bind it to the repeater to display the chapter data
repMenuItems.DataSource = chapterData;
repMenuItems.DataBind( );
}
} // Page_Load
} // CH06ApplicationStateCS }