- Day 3: Using Web Forms
- Performing Basic State Management in Web Applications
- Using Cookies
- Using Hidden Fields and Query Strings
- Working with the Session Object
- Working with the Application Object
- Setting Up Global Objects with the global.asax File
- Configuring the Application
- Summary
- Q&A
- Workshop
Setting Up Global Objects with the global.asax File
ASP.NET uses a special file, called global.asax, to establish any global objects that your Web application uses. The .asax extension denotes an application file rather than .aspx for a page file.
Each ASP.NET application can contain at most one global.asax file. The file is compiled on the first page hit to your Web application. ASP.NET is also configured so that any attempts to browse to the global.asax page directly are rejected.
Listing 3.8 shows a global.asax file that you can use to make a more complete hit counter.
Listing 3.8 global.asax: Event Handlers for the Application and Session Objects
1: <%@ language="C#" %> 2: <script runat="server"> 3: void Application_Start(Object Sender, EventArgs e) 4: { 5: Application["Hits"] = 0; 6: Application["Sessions"] = 0; 7: Application["TerminatedSessions"] = 0; 8: } 9: 10: //The BeginRequest event is fired for every hit to every page in the site 11: void Application_BeginRequest(Object Sender, EventArgs e) 12: { 13: Application.Lock(); 14: Application["Hits"] = (int) Application["Hits"] + 1; 15: Application.UnLock(); 16: } 17: void Session_Start(Object Sender, EventArgs e) 18: { 19: Application.Lock(); 20: Application["Sessions"] = (int) Application["Sessions"] + 1; 21: Application.UnLock(); 22: } 23: 24: void Session_End(Object Sender, EventArgs e) 25: { 26: Application.Lock(); 27: Application["TerminatedSessions"] = 28: (int) Application["TerminatedSessions"] + 1; 29: Application.UnLock(); 30: } 31: 32: void Application_End(Object Sender, EventArgs e) 33: { 34: //Write out our statistics to a log file 35: //...code omitted... 36: } 37: </script>
The global.asax file in Listing 3.8 contains event handlers for the Session and Application objects. Each event handler has the same signature as the Page_Load event handler.
The code in Listing 3.8 handles three Application object-related events: Start (Lines 3-8), End (Lines 24-30), and BeginRequest (Lines 11-16). Start and End are called when the Web application starts and ends, respectively. BeginRequest is called for every page hit that the site receives. Listing 3.8 updates the total number of hits on this event.
The Session Start (Lines 17-22) and End (Lines 24-30) events are handled in the middle of the listing. These two events count how many different Web users have accessed the site.
You can write a simple page to utilize the statistics that Listing 3.8 tracks. Listing 3.9 shows a page that writes out the results of the hit-counting code in the global.asax file. Figure 3.5 shows the Statistics page after a few hits.
Listing 3.9 Statistics.aspx: The Results of the Tracking in the global.asax File
<%@ page language="C#" %> <html> <body> <h2> Statistics for the Test Web Application </h2> Total hits: <% Response.Write(Application["Hits"].ToString()); %> <br> Total sessions: <% Response.Write(Application["Sessions"].ToString()); %> <br> Expired sessions: <% Response.Write(Application["TerminatedSessions"].ToString()); %> <br> </body> </html>
TIP
If the global.asax file is modified, the Web application is restarted on the next page hit, and the global.asax file is recompiled.
Figure 3.5 The Statistics page after some traffic.
Adding Objects to the global.asax File
To use global objects in your ASP.NET application, add the <object> tag in the global.asax file for each one. The <object> tag has an optional attribute called scope, which determines if the added object will be created on-the-fly, associated with the Application object, or associated with the Session object.
To explore the <object> tag, let's create a simple class that stores and retrieves strings. The sample is going to associate an object of this class with the Application object in the global.asax file, so the class must be thread-safe. The term thread-safe means that many client threads can access the class at the same time without any data corruption. Because ASP.NET uses one thread per page, ensuring that the class is thread-safe is critical if multiple users browse the site at the same time.
Understanding Threads
What's a thread? To answer, let's review processes first. All Windows applications are processes that run on your computer. Processes contain their own code and memory space, and can interact with computer peripherals, such as the screen or the network card. ASP.NET runs as a process, and it executes your code, of course.
Each process contains one or many threads. A thread is like a process or an individual program because it also executes a certain set of code. However, a thread is a "lightweight" version of a process. Threads live inside processes and use a process' memory. The Windows operating system gives each thread a small amount of time to execute and quickly switches between threads so that it seems like more than one thread is executing at the same time. For all practical intents, the threads are running at the same time.
Because threads use their parent process' memory, they can potentially change the same object (in memory) at the same time. For two threads, A and B, thread A might add 10 to a counter object. Thread B might subtract 10 from the counter. If the two threads are switched on and off by the operating system in an "unlucky" way, the counter object could contain a scrambled result.
Each ASP.NET page, when it is being processed, gets its own thread. If more than one user uses the Web site at the same time, many threads will appear, even if both users are accessing the same ASP.NET page.
To prevent threads (and ASP.NET pages) from interfering with each other when accessing the same object, use the technique in the example that follows.
To make the class thread-safe, use the Synchronized method of the base collection class, Hashtable. This class is shown in Listing 3.10.
Listing 3.10 MyClass.cs: Implementing a Class to Store and Retrieve Strings in a Thread-Safe Way
using System; using System.Collections; namespace TestApplication { public class MyClass { private Hashtable m_col; //m_colSync will be a thread-safe container for m_col private Hashtable m_colSync; public MyClass() { m_col = new Hashtable(); m_colSync = Hashtable.Synchronized(m_col); } public void AddItem(String Name, String Value) { m_colSync[Name] = Value; } public String GetItem(String Name) { return (String) m_colSync[Name]; } } } //note: use "csc /out:bin\myclass.dll /t:library myclass.cs /r:system.dll" // to compile with the command line utility
The next step is to add this object to the global.asax file with the <object> tag. A short global.asax file that does this follows:
<object id="StringCollection" runat="server" class="TestApplication.MyClass" scope="Application" />
The id attribute tells ASP.NET what to call our object when it's used later. The class attribute can be used to specify COM objects in addition to .NET components, but by using their ProgID.
If the listing omitted the scope attribute, a new object is created on-the-fly for every page that uses the StringCollection object.
Let's write a sample page that uses StringCollection. Listing 3.11 shows just such a page.
Listing 3.11 UseObject.aspx: Using the Global Object
<%@ language="C#" %> <script runat="server"> void Page_Load(Object Sender, EventArgs e) { StringCollection.AddItem("FirstUser", "Joe Smith"); } </script> <html> <body> The name of the first user is <% Response.Write(StringCollection.GetItem("FirstUser")); %> </body> </html>
Putting Code Behind the global.asax File
If you use Visual Studio.NET to create your Web project, it will use the code behind feature of ASP.NET for global.asax. The code behind file that is generated is named global.asax.cs, when using the C# compiler. To use code behind in global.asax manually, use the Application directive instead of the Page directive, like this:
<@ Application Inherits="MyApplication.GlobalClass" %>
Listing 3.12 shows a code behind example for the global.asax file.
Listing 3.12 GlobalClass.cs: Implementing the Code for the global.asax File
namespace MyApplication { using System; using System.Web; using System.Web.SessionState; public class GlobalClass : System.Web.HttpApplication { protected void Session_Start(Object Sender, EventArgs e) { Response.Write("Session started <br>"); } } } //note: use "csc /out:bin\globalclass.dll /t:library globalclass.cs /r:system.dll" // to compile with the command line utility
TIP
You can mix code behind and the <object> tag in the global.asax file.