Application Events


Although we don't have to use events in ASP.NET, they do make our lives easier. Events provide great ways to organize and control execution of code. Examples of this include creating instances of objects that you assign to Application , Cache , or Session when your application starts, validating custom user credentials before you allow the user to access the requested resource, or perhaps, implementing a billing feature that will charge the user for each access to a page. The options are endless. The point is that application events allow you to execute your own code while ASP.NET processes the request. We can use application events in one of two ways:

  • Implement event prototypes in global.asax :We will simply add the event prototypes file. This is similar to events we captured in ASP's global.asa file such as Application_OnStart or Session_OnEnd . We will use global.asax to demonstrate application events in this chapter.

  • Author custom HTTP modules :An HTTP module is an advanced feature of ASP.NET. It is the equivalent of IIS's ISAPI filter concept. An HTTP module gives us an opportunity to work with the request before it is serviced by an ASP.NET page or web service (or custom HTTP Handler), and again, before the response is sent to the client. For example, we can use HTTP modules to author custom solutions for our ASP.NET applications, such as an authentication system that authenticates users against a Netscape LDAP.

    Important

    ASP.NET application events are multi-cast events. This means that we can have both an HTTP module and global.asax respond to the same event.

ASP.NET supports 18 application events, and also allows you to add your own custom events. ASP.NET also introduces support for asynchronous events. We will discuss these at the end of the chapter in the Advanced Topics section.

Event Syntax and Prototypes

When implementing the event code in global.asax , it is a good practice to use sender as the Object parameter, and e as the EventArgs parameter. The syntax for Visual Basic .NET:

  Public Sub Application_OnStart(sender As Object, e As EventArgs)   End Sub  

And in C#:

  public void Application_OnStart(Object sender, EventArgs e) {   }  

The argument provided to your event prototype tells you who raised the event ( sender ). It also provides a mechanism for the sender to provide additional event details through an EventArgs parameter ( e ).

In addition to using this prototype, you can also use a shorthand event prototype (shorthand since you're not naming the event parameters). The code in VB.NET is:

  Public Sub Application_OnStart()   End Sub  

And in C#:

  public void Application_OnStart() {   }  

In the preceding event prototypes, you do not have access to the EventArgs or the sender . Including the parameters is considered best practice. However, remember that the shorthand syntax is supported.

Supported Events

The 18 supported events can be divided into two categories:

  • Events that are raised on each request.

  • Conditional events, such as when an error occurs.

The next section lists the two categories of events and provides a brief description of each event. We have also provided a figure that shows the ordering of the events for the per-request events. We will soon implement examples using several events.

Per-Request Application Events

Per-request application events are those raised during each and every request made to an ASP.NET application, such as the events that indicate the beginning or end of the request. Some of these include:

  • Application_OnBeginRequest :This event is raised on each request that ASP.NET handles, for example, a page or web service. This is unlike the familiar ASP Application_OnStart event, which is only raised once when the application is started. We can use the Application_OnBeginRequest event to execute code before a page, web service, or any other HTTP Handler gets the opportunity to process the request.

  • Application_OnAuthenticateRequest :This event is raised when ASP.NET is ready to perform authentication on the request (see Chapter 14 for more detail on authentication). Events such as these allow us to easily build custom authentication systems in ASP.NET. Within this event, we can examine the request and execute our own code to determine whether or not the request is authenticated. When enabled, ASP.NET authentication modes such as Windows Forms or Passport use this event.

  • Application_OnAuthorizeRequest :Similar to OnAuthenticateRequest , this event is raised when ASP.NET is ready to authorize a request for a resource. We can use this event to examine the request and execute our own code that determines what privileges we grant or deny the request. Similar to the previous event, when enabled, the ASP.NET authorization system relies on this event for its authorization support.

  • Application_OnResolveRequestCache :Although not yet discussed, ASP.NET has a rich page and web service output caching feature that utilizes the Cache covered earlier in this chapter. For example, rather than executing a page on each request, the page can be executed once and served statically for future requests . This event is raised when ASP.NET is ready to determine if the request should be served from the cache. Internally, ASP.NET's output cache relies upon this event. We can use it to run application code independently of whether or not the actual response is served from the output cache.

  • Application_OnAcquireRequestState :This event is raised when ASP.NET is ready to acquire Session state data from in-process, out-of-process Windows Service, or SQL Server. If we want to provide our own 'custom' Session , such as an XmlSession object, we could populate the values of that object using this event. Then, when the request is handed to the page or web service, the XmlSession would already have its values populated .

  • Application_OnPreRequestHandlerExecute :This event is raised just before the handler servicing the request is called. In most cases, the handler will be the Page handler.

    Note

    After the Application_OnPreRequestHandlerExecute event is raised, the HTTP Handler receives the request. The next application event is raised when the handler is finished with the request.

  • Application_OnPostRequestHandlerExecute :This is the first event raised after the handler has completed servicing the request. The Response object now has data to be sent back to the client.

  • Application_OnReleaseRequestState :This releases the Session data and updates storage if necessary. After this event is raised, we can no longer update Session data. In the corresponding Application_OnRequestState event, we mentioned populating an XmlSession object. In this corresponding ReleaseState event, we could write the value of XmlSession back to the XML file that represented the session data for a particular user.

  • Application_OnUpdateRequestCache :This event is raised when ASP.NET updates the output cache with the current request (if it is to be output cached).

  • Application_OnEndRequest :Once the request is complete, this is the last event raised that allows us to affect the application response before we send the HTTP headers and body.

Figure 12-11 shows the processing of a request by ASP.NET using the ten events just detailed:

click to expand
Figure 12-11:

As shown, IIS first receives the request and then hands it to ASP.NET. The ASP.NET Application Events are then raised, starting with Application_OnBeginRequest . Immediately before an HTTP Handler (such as default.aspx ) is called, the Application_OnPreRequestHandlerExecute event is raised. Immediately after the HTTP Handler has executed the Application_OnPostRequestHandlerExecute is raised. Finally, the Application_OnEndRequest event is raised before the response is handed back to IIS to be sent back to the requestor ,.

These ten per-request events are raised in a known order. However, there are two other per-request events that are raised during the processing of the request.

Per-Request Indeterminate Order

Unlike the other per-request events, the following two events are raised as soon as data is ready to be sent back to the client. By default, ASP.NET enables response buffering “ this simply means the server will not begin sending data to the requestor until all the data is ready. When buffering is enabled, the following two events are raised after Application_OnEndRequest . However, if buffering is disabled, the response can be sent as data becomes available. In that case, the following two events will be raised when the data is sent to the client.

  • Application_OnPreSendRequestHeaders :This event is raised before the HTTP headers are sent to the client making the request. Once the headers are sent, we cannot modify the content of the response “ we have already sent the content size of the response to the client.

  • Application_OnPreSendRequestContent :This event is raised before the HTTP body is sent to the client making the request.

Conditional Application Events

Conditional application events are events that may or may not be raised during the processing of a request. For example, when the application starts, we raise the Application_OnStart event, or when an error occurs within our application, we raise the Application_Error event. These events are just as useful as our per-request events, sometimes even more so:

  • Application_OnStart :This event is raised when an ASP.NET application first starts unlike Application_OnBeginRequest that is raised on each request. We can use this event to carry out tasks that prepare our application to service requests. Examples include opening a connection to the database and retrieving some shared data, adding items to the cache, or simply setting application or static variables to default values. If this event has not yet been raised when a request comes in, it will be raised before the per-request Application_OnBeginRequest event.

  • Application_OnEnd :This event is another single occurrence event. It is the reciprocal event to Application_OnStart as it is raised when the ASP.NET web application is shutting down. We can use this event for cleaning up code. For example, closing connections to the database, evicting items from the cache, or resetting Application and static variables.

  • Most of these tasks won't be necessary, however, since once the application ends, the CLR will eventually release the application's memory. However, it is still good practice to do the cleanup ourselves .

  • Session_OnStart :This event is raised when a user's session begins within an ASP.NET application. We can use this event to execute code that is user specific, such as assigning values to Session .

  • Session_OnEnd :This is the reciprocal event to Session_OnStart , being raised when a user's Session ends. If we wish to save the Session data, we can use this to walk the object and save interesting information to a SQL database, or other permanent storage medium.

  • Application_Error :This event is raised whenever an unhandled application error occurs. This is a very powerful event, and we will call it in an example later in the chapter. Just as an ASP.NET page supports a Page_Error for unhandled page exceptions, the Application_Error allows us to catch all unhandled exceptions for the entire application, such as logging an exception to the Windows event log, or sending the administrator an e-mail containing details of the error.

  • Application_OnDisposed :This event is raised when the ASP.NET application is eventually shut down and the CLR removes the application from memory. This event gives us the opportunity to clean up or close any outstanding connections or write any last data back to a database or file system. In most scenarios, this event will not be used.

Although ASP.NET supports 18 events, you don't necessarily have to use all of them. Next, you will look at some examples of the common events that you will want to use most often in ASP.NET applications.

Event Examples

The following code samples demonstrate Application events.

Adding a Footer to All Pages

The Application_OnEndRequest event is raised at the end of the request immediately before the response is sent to the requestor. Since this event is the last called, you can use it to run some code before the response is sent, or to modify the response. For example, an ISP running ASP.NET could use the Application_OnEndRequest event to add a footer to the bottom of all pages served by ASP.NET.

Here is the global.asax code in Visual Basic .NET:

  <Script runat="server">     Public Sub Application_OnEndRequest(sender As Object, e As EventArgs)   Response.Write("<hr size=1>")   Response.Write("<font face=arial size=2>This page was " _   & "served by ASP.NET</font>")   End Sub     </Script>  

In the preceding global.asax code, we implemented the Application_OnEndRequest event and within the event used Response.Write to output a simple statement that says This page was served by ASP.NET.

We can then author an ASP.NET page in Visual Basic .NET:

  <%@ Import Namespace="System.Data" %>   <%@ Import Namespace="System.Data.SqlClient" %>     <Script runat="server">     Public Sub Page_Load(sender As Object, e As EventArgs)   Dim connection As SqlConnection   Dim command As SqlCommand   Dim reader As SqlDataReader   Dim sqlSelect As String   Dim dsn As String     ' Name dsn and sql select   dsn="server=localhost;uid=sa;pwd=;database=pubs"   sqlSelect="Select * From stores"     ' Connect to the database   connection = New SqlConnection(dsn)   command = New SqlCommand(sqlSelect, connection)     ' Open the connection   connection.Open()   ' Create the reader   reader = command.ExecuteReader()     ' Populate the datagrid   datagrid1.DataSource = reader   datagrid1.DataBind()     ' Close the connection   connection.Close()   End Sub     </Script>   <asp:datagrid id="datagrid1" runat="server" />  

The preceding code connects to a database and populates an ASP.NET datagrid with the results. If you save these two files into a web application, the code executed in Application_OnEndRequest will be added to the bottom of the requested page, as shown in Figure 12-12:

click to expand
Figure 12-12:

When you enable page tracing (which we'll learn more about in the following chapter), you output trace details at the end of the request. Internally, tracing is using the Application_OnEndRequest to ensure that the output details are the final addition to the request.

In this example, we are using an event to output code, to better illustrate how the event is executed. This works well for ASP.NET pages because the Response.Write statements are appended to the end of the page results and simply add additional HTML. However, remember that ASP.NET is no longer simply about displaying HTML pages. Good examples here include the rich set of server controls that can output WML, or ASP.NET Web services that return XML to the caller. The global.asax file is global for the ASP.NET application, and if you want to add logic into your global.asax file that outputs HTML, you should probably not serve ASP.NET Web services out of that same application, unless you add additional code that checks the request type to see if HTML content can be returned.

Loading Custom User Data

ASP.NET provides some great facilities for storing per-request user state, Session , and per-application state “ Application and Cache . However, what if you wanted to also support a scenario that fell somewhere in-between these, such as per-request group data?

Many sites personalize themselves based on the identity of the requestor. What if you didn't want to personalize for an individual user, but instead wanted to group a common set of users together and personalize your site based on group settings? For example, what if you divided your users into groups such as Gold, Silver, and Bronze. You want Gold customers to have access to the common state, but not so for Silver and Bronze customers. Similarly, you would want your Silver customers to only see their data.

We can easily build our own 'state manager' using ASP.NET and use the Application_OnAcquireRequestState event to determine who the request is for and then to fetch the appropriate data. Let's look at a simple example that identifies the customer category from the URL: default.aspx?customerType=Gold, default.aspx?customerType=Silver, and so on.

First, let's code the Visual Basic .NET global.asax file:

  <%@ Import Namespace="System.Xml" %>   <Script runat="server">     Public Sub Application_OnAcquireRequestState( _   sender As Object, e As EventArgs)   Dim dom As New XmlDocument()   Dim customerType As String   ' Grab the customerType from the QueryString   customerType = Request.QueryString("customerType")     ' Check for values   If (IsNothing(customerType)) Then   customerType = "Bronze"   End If     ' Load the appropriate XML file   Select Case customerType   Case "Gold"   dom.Load(Server.MapPath("Gold.xml"))   Case "Silver"   dom.Load(Server.MapPath("Silver.xml"))   Case Else   dom.Load(Server.MapPath("Bronze.xml"))   End Select     Session("WelcomeMsg") = _   dom.SelectSingleNode("/customer/welcome").InnerText   End Sub     </Script>  

The file's Application_OnAcquireRequestState event begins by executing some logic to determine where the current request fits in “ we are simply passing a customerType value on the QueryString “ and if no customerType is provided, we default to Bronze.

Then the code loads an XML file for the appropriate customer type, for example, Gold.xml , and loads the welcome message from that file. Here are the Gold.xml , Silver.xml , and Bronze.xml files:

  <?xml version="1.0"?>     <customer>   <welcome>   You're a Gold customer -- you get a free product sample!   </welcome>   </customer>     <?xml version="1.0"?>     <customer>   <welcome>   You're a Silver customer -- thanks for your business!   </welcome>   </customer>     <?xml version="1.0"?>     <customer>   <welcome>   You're a Bronze customer -- can we interest you in 30 days no interest?   </welcome>   </customer>  

Finally, a session value is set for the current request, Session("WelcomeMsg") , with the appropriate welcome message for the customer type.

We can then write an ASP.NET page that extracts this session value and displays the welcome message for the correct group:

  <Script runat="server">   Public Sub Page_Load(sender As Object, e As EventArgs)   WelcomeMsg.Text = Session("WelcomeMsg")   End Sub   </Script>   <asp:label id="WelcomeMsg" runat="server" />  

Although this is a simple example (we could have written all of this code into our ASP.NET page), it shows how nicely you can encapsulate this inside global.asax and not repeat it on each and every application file that wants to use the associated XML file for the customer type. Additionally, when users access the ASP.NET page, the values are already populated.

Finally, let's look at the Application_Error event that you can use to catch unhandled exceptions from your ASP.NET application.

Handling Application Errors

Since ASP.NET uses the CLR, you can use any CLR language to build your web application. One of the CLR's features is structured try / catch exception handling (no more of VB6's On Error Resume Next !). As great as this new structured error handling model is, it doesn't prevent you from writing buggy code. For example, you might write some code in your ASP.NET application that connects to and reads from a database. You can also wrap that code in a try / catch block so that if you can't connect to the database, you can handle the error appropriately.

However, what happens if an exception occurs outside of a try / catch block? If it is not handled, ASP.NET will throw a run-time error (providing you with a detailed overview of where the error occurred and what the application was doing). For ASP.NET pages, you can optionally implement a Page_Error event to catch all unhandled page errors. However, if you decide you would rather catch all unhandled ASP.NET errors at the application level, you have that option too, using the Application_Error event.

We can use this event as a catch-all whenever an unhandled exception occurs, and log the exception to the Windows event log:

  <%@ Import Namespace="System.Diagnostics" %>     <script language="VB" runat=server>   Public Sub Application_Error(Sender as Object, E as EventArgs)     Dim LogName As String = "Web_Errors"   Dim Message As String   Message = "Url: " & Request.Path   Message = Message & " Error: " & Server.GetLastError.ToString     ' Create event log if it doesn't exist   If (Not EventLog.SourceExists(LogName)) Then   EventLog.CreateEventSource(LogName, LogName)   End if     ' Fire off to event log   Dim Log as New EventLog     Log.Source = LogName   Log.WriteEntry(Message, EventLogEntryType.Error)     End Sub   </script>  

In this example, we first import the namespace System.Diagnostics as we will be using some of the classes found in this namespace to write to the event log. We then implement our Application_Error event handler and create some local variables. This is done before using the EventLog class's static method SourceExists to determine if the event log you're going to write to already exists “ if it doesn't we create it. Finally, we create a new EventLog instance named Log and use the WriteEntry method to enter our Message into the Windows Event Log.

Whenever an error occurs within our application, that error is now logged into a custom event log named Web_Errors . Note that we wrote approximately 10 lines of code to accomplish a task that could potentially be 50 to 60 lines in VB/ASP!

Now that we have covered each of the application events, let's look at some advanced topics. These are areas that are left to the more advanced ASP.NET developer and the understanding of these topics is not required to build great ASP.NET applications, but they do help!




Professional ASP. NET 1.1
Professional ASP.NET MVC 1.0 (Wrox Programmer to Programmer)
ISBN: 0470384611
EAN: 2147483647
Year: 2006
Pages: 243

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