4.3 Applications


The first point of extensibility in the HTTP pipeline that we explore is the application class. In any ASP.NET application, the HttpApplication class plays a vital role in nearly every aspect of processing requests . It acts as the initial entry point for a request to a particular application; it serves as a repository of globally available resources in an application, such as application state, the cache, and session state; and it provides access to many important events that occur during the lifetime of an application. You can make use of many of these features by simply using the HttpApplication instance that is created implicitly for you when your application starts. It is accessible through the HttpContext class and the Page class via their ApplicationInstance properties.

You can also customize the application class associated with your application by creating a class that derives from HttpApplication and intercepting events or adding whatever supplemental functionality you desire . For ASP.NET to know about and use your application class, you must create a file called global.asax , located at the top of the virtual directory associated with your application. Like .aspx files, this file is parsed and compiled into an assembly when the application is first accessed. In this case, the class created derives from HttpApplication . Listing 4-2 shows a sample global.asax file with several commonly defined event handlers. Note that the Application directive is used at the top of the file instead of the Page directive used by .aspx files. Also notice that the page consists primarily of a server-side script block, which contains of a number of method definitions. These methods are specially named such that when the class is created, they are automatically wired up as handlers to the corresponding events.

Listing 4-2 A Sample global.asax File
 <%! file: global.asax %> <%@ Application Language="C#" %> <script runat=server> protected void Application_Start(object src, EventArgs e) { } protected void Session_Start(object src, EventArgs e) { } protected void Application_BeginRequest(object src,                                         EventArgs e) { } protected void Application_EndRequest(object src,                                       EventArgs e) { } protected void Application_AuthenticateRequest(object src,                                                EventArgs e) { } protected void Application_Error(object src, EventArgs e) { } protected void Session_End(object sender, EventArgs e) { } protected void Application_End(object src, EventArgs e) { } </script> 

As with .aspx files, you can use code-behind for global.asax files if you prefer to precompile your HttpApplication -derived class. If you are working with Visual Studio .NET, you will notice that when you create a new ASP.NET Web application project, it creates a new global.asax file for you with an associated code-behind class. Listing 4-3 shows a global.asax file that uses code-behind, which is shown in Listing 4-4. Also, as with the Page directive, you can elect either to precompile the code-behind class or to use the src attribute to have ASP.NET automatically compile the file into a class for you. You may notice that Visual Studio .NET again uses the CodeBehind attribute on the Application directive, even though technically this is not an attribute recognized by ASP.NET. This attribute is used exclusively by Visual Studio .NET to create the association between the .asax file and its code-behind file in the designer. As with pages, Visual Studio .NET uses the precompiled model when working with application classes.

Listing 4-3 Using Code-Behind with global.asax
 <%! file: global.asax %> <%@ Application Inherits="MyApp" %> 
Listing 4-4 Code-Behind File for global.asax
 //file: myapp.cs using System; using System.Web; using System.Web.UI; public class MyApp : HttpApplication {   //... } 

Typically, you would create a custom application class to add handlers to application-level events that occur during request processing. For example, you may want to trap the BeginRequest and EndRequest notification events issued for every request that is handled by an application, perhaps to calculate the time it takes to process each request. Listing 4-5 shows a sample global.asax file that traps the BeginRequest and EndRequest events to calculate the time taken to process each request. In the EndRequest handler, once the time has been calculated, it prints out a footer to the response buffer to show the time it took at the bottom of each page.

Listing 4-5 Tracking Request Time in a global.asax File
 <%! file: global.asax %> <%@ Application Language="C#" %> <script language="C#" runat=server> protected void Application_BeginRequest(object sender,                                         EventArgs e) {   this.Context.Items["startTime"] = DateTime.Now; } protected void Application_EndRequest(object sender,                                       EventArgs e) {   DateTime dt = (DateTime)this.Context.Items["startTime"];   TimeSpan ts = DateTime.Now - dt;   this.Context.Response.Output.Write(     "<br/><font size=1>request processing time: {0}</font>",     ts); } </script> 

It is worth noting the usage of the Items collection of the HttpContext class shown in the previous example. As mentioned earlier, the Items collection is a generic property bag of data that is maintained by the HttpContext class on a per-request basis. It can be used as a place to save data that is used by elements in the pipeline for a particular request. It is critically important that you not store data like this in fields of your HttpApplication -derived class, because as we will see in more detail later in this chapter, each request to a particular application may have a distinct instance of the application class created (or more likely, drawn from a pool). As soon as you begin adding state to your application class, it can quickly become confusing as one request is handed an application class with state initialized from a previous one. So as a general rule, never store instance-state in an HttpApplication -derived class, or if you do, take care to reinitialize that state with each request. Alternatively, you can rely on one of the many state repositories available to you in the pipeline, such as the Items collection of the HttpContext class, the application-wide Cache object, or the per-client session state bag.

4.3.1 Application Events

A number of events are available in the HttpApplication class. Table 4-2 shows the events that are exposed by HttpApplication , most of which are issued with each request that is processed by your application, the two exceptions being the Error and Disposed events. To add a handler for any one of these events, you can either explicitly wire up a delegate to the event during the initialization of your application, or you can define a method whose name is of the form "Application_ event ," and it will be wired up automatically at runtime.

Table 4-2. Events Exposed by HttpApplication

Event

Reason for Firing

Order

BeginRequest

New request received

1

AuthenticateRequest

Security identity of the user has been established

2

AuthorizeRequest

User authorization has been verified

3

ResolveRequestCache

After authorization but before invoking handler, used by caching modules to bypass execution of handlers if cache entry hits

4

AcquireRequestState

To load session state

5

PreRequestHandlerExecute

Before request sent to handler

6

PostRequestHandlerExecute

After request sent to handler

7

ReleaseRequestState

After all request handlers have completed, used by state modules to save state data

8

UpdateRequestCache

After handler execution, used by caching modules to store responses in cache

9

EndRequest

After request is processed

10

Disposed

Just before shutting down the application

-

Error

When an unhandled application error occurs

-

PreSendRequestContent

Before content sent to client

-

PreSendRequestHeaders

Before HTTP headers sent to client

-

In addition, you can wire up handlers to a number of events that occur at different times during the lifetime of an application. The only way to wire up handlers to these particular events is to define methods with the names that match the events, because they are invoked internally by other classes in the pipeline and are not exposed directly as events on the HttpApplication class. Table 4-3 shows the list of additional events, none of which are issued on a per-request basis, for which you can add handlers.

Table 4-3. Additional Events Available through global.asax

Event

Reason for Firing

Application_Start

Application starting

Application_End

Application ending

Session_Start

User session begins

Session_End

User session ends

In addition to the events exposed by the HttpApplication class and global.asax , several properties and methods are worth noting. Listing 4-6 shows the class with some of its more commonly used members . Note that there are property accessors to all the core elements of the pipeline, including the application state bag, the context, the request, the response, the session state, and the server. The virtual Init() method can be overridden in derived classes to perform initialization and is called once in the lifetime of your HttpApplication -derived class, after all modules for that application have been added. Another interesting method is CompleteRequest() , which can be called at any time during request processing to preemptively terminate a request.

Listing 4-6 Members of the HttpApplication Class
 public class HttpApplication : IHttpAsyncHandler, IComponent {  // Properties   public HttpApplicationState Application {get;}   public HttpContext          Context     {get;}   public HttpModuleCollection Modules     {get;}   public HttpRequest          Request     {get;}   public HttpResponse         Response    {get;}   public HttpServerUtility    Server      {get;}   public HttpSessionState     Session     {get;}   public IPrincipal           User        {get;}    // Methods   public virtual void Init();   public void CompleteRequest();    // ... } 

4.3.2 Declarative Object Creation

In addition to defining methods within server-side script blocks in global.asax files, you can also define instances of classes by using the object tag. You can use the object tag to create either .NET classes or COM classes (accessed via interoperability) and can select the scope (either session or application) at which you would like the object to live. Listing 4-7 shows a sample object declaration within a global.asax file.

Listing 4-7 Using the object Tag in global.asax
 <%! File: global.asax %> <object id="MyGlobalCollection" runat="server"  scope="application" class="System.Collections.ArrayList" /> 

Using the object tag in global.asax does two things for you. First, it creates a read-only property using the name specified with the id attribute in your HttpApplication -derived class, which on first access instantiates the class and stores it in either the HttpApplicationState.StaticObjects or the HttpSessionState.StaticObjects collection, depending on the scope. The second thing it does is to add a read-only property to every Page -derived class created by .aspx file compilation, so that you can easily reference the object from every page in your application.

The declarative object creation syntax is in place largely to ensure that traditional ASP applications that use this same syntax will still work the same way. In the new ASP.NET world of class-based programming, the idea of implicitly adding properties to access a global object to every page is somewhat distasteful and should probably be avoided. Prefer instead to work directly with the session state bag, discussed in detail in Chapter 10, or for application-wide objects, the Cache , discussed in detail in Chapter 9.



Essential ASP.NET With Examples in C#
Essential ASP.NET With Examples in C#
ISBN: 0201760401
EAN: 2147483647
Year: 2003
Pages: 94
Authors: Fritz Onion

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net