ASP.NET

Overriding HttpApplication

Overriding the HttpApplication to include your own state and event handling is a matter of adding a file named Global.asax to your application. In fact, Visual Studio will add one to your application that is set up and ready to handle a few application-wide events. Remember from examining ASPX files that Page files include the Page directive at the top of the file. The Global.asax file includes a similar directive. The Application directive tells the runtime compiling machinery that this file is meant to serve as the application object.

Listing 17-1 shows an example of the HttpApplication expressed within a file named Global.asax. The Global.asax provided by Visual Studio overrides the Application_Start, Application_End, Application_Error, Session_Start, and Session_End events.

Listing 17-1
<%@ Application Language="C#" %>

<script runat="server">

    void Application_Start(Object sender, EventArgs e) {}
    void Application_End(Object sender, EventArgs e) {}
    void Application_Error(Object sender, EventArgs e) {}
    void Session_Start(Object sender, EventArgs e) {}
    void Session_End(Object sender, EventArgs e) {}

</script>

To get an idea as to how these events work, the following example illustrates placing a piece of data in the application's dictionary and retrieving it later when the page loads.

Managing Application State

  1. Start a new Web site named UseApplication.

  2. Drag a GridView onto the default page. Don't assign a data source to it yet. You'll populate it with a data that is stored with the application in later steps.

  3. Add a Global.asax to the site. Right-click on the project in the Project Explorer (or select Web Site | Add New Item from the main menu). Choose the Global application template, as shown below.

    Graphic
  4. You've just added a file named Global.asax to your application. You can see that the Application_Start event is already handled (although it does nothing right now).

  5. To have some data to store with the application object, import the QuotesCollection from Tutorial 14. The project name is UseDataCaching. Select Web Site | Add Existing Item from the main menu and find the file QuotesCollection.cs. In addition to importing the QuotesCollection.cs file, grab the QuotesCollection.xml and QuotesCollection.xsd files from the UseDataCaching\App_Data directory.

  6. Add some code to the Application_Start event to load the quotes data and place it in the application dictionary. Server.MapPath will give you the path from which the application is executing so you can load the XML and XSD files. Storing the data in the dictionary is very much like adding it to the cache.

        void Application_Start(Object sender, EventArgs e) {
            QuotesCollection quotesCollection = new QuotesCollection();
    
               String strAppPath = Server.MapPath("");
    
               String strFilePathXml =
                    strAppPath  + "\\app_data\\QuotesCollection.xml";
              String strFilePathSchema = strAppPath +
                     "\\app_data\\QuotesCollection.xsd";
    quotesCollection.ReadXmlSchema(strFilePathSchema);
    quotesCollection.ReadXml(strFilePathXml);
    
    Application["quotesCollection"] = quotesCollection;
    }
    
  7. Update Page_Load method in the Default.aspx page to load the data from the application's dictionary. The application state is available through the page's reference to the Application object. Accessing data within the dictionary is a matter of indexing it correctly. After loading the data from the dictionary, apply it to the DataSource property in the GridView and bind the DataGrid.

        protected void Page_Load(object sender, EventArgs e)
        {
          QuotesCollection quotesCollection =
             (QuotesCollection)Application["quotesCollection"];
    
          GridView1.DataSource = quotesCollection;
          GridView1.DataBind();
        }
    

Application State Caveats

As you can see, the application state and the cache seem to overlap in their functionality. Indeed, they're both available from similar scopes (from any point in the application), and getting the data in and out involves using the right indexer. However, the application state and the cache vary in a couple of significant ways.

First, items that go into the application state stay in the cache until you remove them explicitly. The cache implements more flexibility in terms of setting expirations and other removal/refresh conditions.

In addition, putting many items into the application dictionary will inhibit the scalability of your application. To make the application state thread-safe, the HttpApplicationState class has a Lock method. While using the Lock method will ensure the data is not corrupted, locking the application frequently will greatly reduce the number of requests it can handle.

Ideally, data going into the application state should be read only once loaded-or changed very infrequently. As long as you're aware of these issues, the application state can be a useful place to store information required by all parts of your application.

Handling Events

The other useful aspect of the application object is its ability to handle application-wide events. As we saw in the previous example, the Global.asax file is a handy place to insert event handlers. Visual Studio will insert a few for you when you simply add one to your application. Some events are handled only in Global.asax, while others may be handled outside Global.asax. The events for which Visual Studio generates stub handlers inside Global.asax include Application_Start, Application_End, Application_Error, Session_Start, and Session_End. Following is a rundown of these events.

Application_Start

Application_Start happens when the application is first initialized, that is, when the first request comes through. Because Application_Start happens first (and only once) during the lifetime of an application, the most common response for the event is to load and initialize data at the start of the application (as with the example above).

Application_End

The ASP.NET runtime raises Application_End as the application is shutting down. This is a useful place to clean up any resources requiring special attention for disposal.

Application_Error

Unfortunately, bad things sometimes happen inside Web applications. If something bad has happened in one of your existing applications, you may already have seen the standard pale yellow and red ASP.NET error page. Once you deploy your application, you probably don't want clients to see this sort of page. Intercept this event (Application_Error) to handle the error.

Session_Start

The Session_Start event occurs when a user makes an initial request to the application, which initializes a new session. This is a good place to initialize session variables (if you want to initialize them before the page loads).

Session_End

This event occurs when a session is released. Sessions end when they time out or when the Abandon method is called explicitly. This event happens only for applications whose session state is being held in-process.

HttpApplication Events

The events listed above are implemented in Visual Studio's default Global.asax. The application object can fire a number of other events. Table 17-1 shows a summary of all the events pumped through the application object.

Table 17-1 Application-wide events.

Event

Reason

Order

Only in Global.asax

Application_Start

Application is spinning up.

Start of app

*

Application_End

Application is ending.

End of app

*

Session_Start

Session is starting.

*

Session_End

Session is ending.

*

BeginRequest

A new request has been received.

1

AuthenticateRequest/

PostAuthenticateRequest

The user has been authenticated, that is, the security identity of the user has been established.

2

AuthorizeRequest/

PostAuthorizeRequest

The user has been authorized to use the requests resource.

3

ResolveRequestCache/

PostResolveRequestCache

Occurs between authorizing the user and invoking handler. This is where the output caching is handled. If content is cached, the application can bypass the entire page rendering process.

4

AcquireRequestState/

PostAcquireRequestState

Occurs when session state needs to be initialized.

5

PreRequestHandlerExecute

Occurs immediately before request is sent to the handler. This is a last-minute chance to modify the output before it heads off to the client.

6

PostRequestHandlerExecute

Occurs following the content being sent to the client.

7

ReleaseRequestState/

PostReleaseRequestState

Occurs following request handling. This event occurs so the system may save state used if necessary.

8

UpdateRequestCache/

PostUpdateRequestCache

Occurs following handler execution. This is used by caching modules to cache responses.

9

EndRequest

Fires after request is processed.

10

Disposed

Occurs before the application shuts down.

End of app

Error

Fired when an unhandled application error occurs.

When an exception occurs

PreSendRequestContent

Fired before content sent to client.

PreSendRequestHeaders

Fired before HTTP headers sent to client.

The following example shows how to time requests by intercepting the BeginRequest and the EndRequest events within Global.asax.

Timing Requests

  1. Open up Global.asax within the UseApplication Web site.

  2. Add handlers for BeginRequest and EndRequest as shown below.

    Graphic

    Visual Studio will insert the following stubs in Global.asax:

        protected voidApplication_BeginRequest(object sender,
                 EventArgs e)
        {
    
        }
    
        protected void Application_EndRequest(object sender,
                 EventArgs e)
        {
    
        }
    
  3. Implement the BeginRequest handler by getting the current date and time and storing it within the Items property of the current HttpContext. The Items property is a name/value collection that you may index in the same way you index the cache, the session state, and the HttpApplication dictionary. Implement the EndRequest handler by comparing the time stamp obtained from the beginning of the request and comparing it to the current date and time. Print out the amount of time taken to process the request using Response.Write.

    protected void Application_BeginRequest(object sender, EventArgs e)
        {
    
            DateTime dateTimeBeginRequest = DateTime.Now;
    
            HttpContext ctx = HttpContext.Current;
            ctx.Items["dateTimeBeginRequest"] = dateTimeBeginRequest;
    
        }
    
        protected void Application_EndRequest(object sender, EventArgs e)
        {
            DateTime dateTimeEndRequest = DateTime.Now;
    
            HttpContext ctx = HttpContext.Current;
            DateTime dateTimeBeginRequest =
                 (DateTime)ctx.Items["dateTimeBeginRequest"];
    
            TimeSpan duration = dateTimeEndRequest - dateTimeBeginRequest;
            Response.Write("<b> This request took " +
                duration.ToString() + "</b></br>");
        }
    

    You should see the duration printed near the top of the response returned to the browser:

    Graphic