Advanced Topics


This section covers four advanced topics related to building superior ASP.NET applications:

  • Using static variables :It is not always necessary to use Application to store persistent values in memory. Since ASP.NET is compiled and the application is represented in an object-oriented manner, we can use global static variables in addition to Application .

  • Using our own base class for global.asax :The earlier discussion of the Application directive for global.asax mentioned the Inherits attribute. We will examine how we can use this to create our own class for global.asax to instantiate.

  • Mapping file extensions :If we want ASP.NET to support file extensions other than the defaults, such as the file extension .wrox , we must map the extension in IIS first. This is because IIS gets the first look at the request and acts as the router determining where to send requests .

  • Asynchronous application events :Earlier in the chapter, we discussed the application events that ASP.NET supports. What we didn't discuss in detail is the fact that ASP.NET also supports some asynchronous representations of these events.

Static Variables

Another supported attribute of the Application directive is Classname . This attribute allows you to control the name of the class generated for your global.asax code when it is compiled. If you provide a Classname value, you can access the instance of global.asax .

Since you now have access to the global.asax instance, this also means that public methods , properties, or variables declared within it are accessible anywhere in your application. An advanced design choice you can elect to make is to take advantage of one of the object-oriented features of ASP.NET “ static members .

When a class is created, such as an instance of global.asax , each instance of the class also uses its own methods, properties, and variables to perform tasks . You can declare a method, property, or variable static and all instances of the class will share the one instance of that method, property, or variable. These static members can be used to store commonly accessed data, for instance, a string array containing the 50 states of the USA.

Using static members is sometimes faster than accessing Application state. Application is an object, and loading Application requires memory allocations , and so on. A simple example that demonstrates this is the discount rate applied to all products for a one-time sale. The following snippet is a sample global.asax file in C#:

  <%@ Application Classname="CommerceApplication" %>     <Script Language="C#" runat="server">   // Set discount to 10%   public static float discountRate = .1F;   </Script>  

And in Visual Basic .NET:

  <%@ Application Classname="CommerceApplication" %>     <Script runat="server">   ' Set discount to 10%   Public Shared discountRate As Single = .1F   </Script>  

We first identify the name of the global.asax 's class using the Classname attribute of the Application directive. Then we declare a static member variable, discountRate .

We can now write code that accesses this class and its static member, discountRate . The following code snippet in VB.NET is a sample ASP.NET page, default.aspx :

  <Script runat="server" >   Public Sub Page_Load(sender As Object, e As EventArgs)   ' Calculate the discount rate   Dim discountRate As Single   Dim productCost As Single     ' Determine productCost value   productCost = 19.99F   ' Calculate discount rate and apply to product cost   discountRate = CommerceApplication.discountRate   productCost = productCost - (productCost * discountRate)     ' Display calculation   lblCost.Text = productCost.ToString()     End Sub   </Script>     The cost of the product is: $<asp:label id="lblCost" runat="server" />  

Here we have a simple Visual Basic ASP.NET page that calculates a product's cost with the applied discount rate. The value for discountRate is obtained from the static member defined in our global.asax file CommerceApplication.discountRate .

Using Your Own Base Class for global.asax

The Inherits attribute of the global.asax application directive allows you to name a .NET class that global.asax will use as the base class for all compiled instances of global.asax . This is useful if you want to add your own methods or properties as part of global.asax . It allows you create a global.asax file that is customized to a particular application.

For example, a commerce solution may provide a commerce-oriented global.asax that exposes properties or methods that are specific to its application, for example, a global.asax property such as AdTargetingEnabled . Developers who use this commerce framework don't see the implementation of this property. Instead, it's encapsulated within global.asax and they just need to know what happens when they set AdTargetingEnabled = true .

Inherits

To use Inherits , you first need to create your own custom class that inherits from the HttpApplication class. HttpApplication is the default base class used by global.asax , and it is what exposes the application and session events as well as any default properties.

After creating a new class that inherits from HttpApplication , and adding the new functionality you desire , you can then use the global.asax Inherits directive to instruct ASP.NET to use your base class instead of HttpApplication . Let's illustrate this with an example.

The following code snippet is a simple class, MyApplication , that inherits from HttpApplication . The MyApplication class implements a CurrentTime method that simply returns the current date/time. Using the Inherits keyword, you have told the compiler that the MyApplication class inherits all the methods, properties, events, and so on, that HttpApplication implements. Essentially, all this class does is add one more method:

  Imports System   Imports System.Web     ' To compile: vbc /t:library /r:system.web.dll /r:system.dll Inherits.vb     Public Class MyApplication   Inherits HttpApplication     Public Function CurrentTime() As String   ' Use ToString("r") to show seconds in Now output   Return DateTime.Now.ToString("r")   End Function     End Class  

Next , you need to compile and deploy the generated assembly to your web application's bin directory. To compile, you can either create a new Visual Basic .NET Class project in Visual Studio .NET, or you can use the command line compilers. Either will work equally well. Here is the command line compiler command to compile this in case you don't have Visual Studio .NET:

  > vbc /t:library /r:system.web.dll /r:system.dll Inherits.vb  

Next, you need to copy the resulting .dll to your web application's bin directory. Remember, deploying it to the bin directory makes it available to your application.

You can then write a global.asax file that uses the Application directive's Inherits attribute to inherit from your custom base class. Then, within your global.asax code, you have access to your new method:

  <%@ Application Inherits="MyApplication" %>     <Script runat="server">   Public Sub Application_OnBeginRequest()     Dim TimeStamp As String   TimeStamp = CurrentTime()   Response.Write("Request Beginning TimeStamp: " & TimeStamp)   Response.Write("<HR size=1>")     End Sub   </Script>  

Since we inherited from the MyApplication base class (which itself inherits from HttpApplication ), we have all of the standard behaviors of global.asax provided with the addition of a new CurrentTime method. In this code example, we created a simple local variable of type String named TimeStamp , then set TimeStamp using the inherited CurrentTime method, before returning the result with Response.Write .

Mapping File Extensions to ASP. NET

A more advanced (but no more difficult) option that ASP.NET supports is mapping custom file extensions to ASP.NET resources. If for example, instead of using the extension .aspx for ASP.NET pages we decided to use the extension .wrox , you would need to make two changes to enable ASP.NET to serve default.wrox :

  • First, we must create the following new entry in the <httpHandlers> section of either our web.config or machine.config files “ more about these two files and the <httpHandlers> settings in the next chapter.

     <configuration>         <system.web>       <httpHandlers>  <add verb="*" path="*.wrox"   type="System.Web.UI.PageHandlerFactory.System.Web" />  </httpHandlers>    </system.web>      </configuration> 
  • Second, we must configure IIS to send requests with the extension .wrox to ASP.NET. This is accomplished through the IIS Microsoft Management Console.

Open the IIS MMC, and right-click on a web root or a web application folder (if you want to limit the mapping to a single application) and select the Properties option. Once the dialog is open , press the Configuration button, and select the App Mappings tab, as shown in Figure 12-13:

click to expand
Figure 12-13:

This tab lists all the extensions that IIS maps to ISAPI extensions. ISAPI is a low-level API that lets custom applications plug in to IIS. ASP used an ISAPI named asp.dll , and ASP.NET uses an ISAPI named aspnet_isapi.dll . The ASP.NET ISAPI simply takes the entire request from IIS and hands it to ASP.NET. If you want ASP.NET to handle the .wrox extension, you need to map it onto the aspnet_isapi.dll so that IIS sends the request to ASP.NET.

To add this application mapping, press the Add button. This brings up the Add/Edit Application Extension Mapping dialog box. You can then name the ASP.NET ISAPI ( aspnet_isapi.dll ), found in the directory C:\[WINNT]\Microsoft.NET\Framework\[version]\ . You can now name your extension .wrox . Your completed entry should look similar to Figure 12-14:

click to expand
Figure 12-14:

In the next chapter, you will look at how to map the .wrox extension to ASP.NET resources through the ASP.NET configuration system.

Asynchronous Application Events

Note

This is a more advanced discussion than the previous topics. Understanding asynchronous application events is not necessary to build good ASP.NET applications. It is, however, an advanced feature that can prove very useful in some cases.

As mentioned earlier, ASP.NET code is executed in an ASP.NET worker process, not in the IIS process. Within this worker process, threads are used to execute code.

A thread is a resource, and there are a finite number of threads that ASP.NET will be able to use, otherwise the processor would spend all its time context-switching (that is, switching threads of execution in the processor) rather than executing user code.

ASP.NET creates and manages a threadpool , increasing and decreasing the number of threads as required throughout the life of the application. This is in contrast to ASP, which used a fixed number of threads.

In some cases, application code such as network I/O can potentially stall threads in the ASP.NET process. This is because the ASP.NET thread has to wait (it is blocked) until this slow operation is completed.

When a thread is blocked, it can't be used to service requests, resulting in queuing of requests and degraded application performance. The ASP.NET team took this into consideration and added support for asynchronous events in addition to the existing synchronous ones discussed earlier.

The only reason for using these asynchronous events in global.asax is in application code (within an event) that performs operations over the network where the network class supports I/O completion ports, such as a web service proxy.

Supported Events

There are ten supported asynchronous events, raised in the following order:

  • AddOnBeginRequestAsync .

  • AddOnAuthenticateRequestAsync .

  • AddOnAuthorizeRequestAsync .

  • AddOnResolveRequestCacheAsync .

  • AddOnAcquireRequestStateAsync .

  • AddOnPreRequestHandlerExecuteAsync .

  • AddOnPostRequestHandlerExecuteAsync .

  • AddOnReleaseRequestStateAsync .

  • AddOnUpdateRequestCacheAsync .

  • AddOnEndRequestAsync .

    Note

    No descriptions are given, as these events are synonymous with their synchronous counterparts described earlier.

When to Use Asynchronous Events

In Chapter 19, you will start looking at ASP.NET Web services. In a nutshell , ASP.NET allows you to easily build XML interfaces for application code. All you need to do is write the application logic and mark the methods with the WebMethod attribute.

Web services are very powerful and easy to use. However, since they make calls over the network, and are subject to all the limitations of that network, we don't want to make a lot of web service calls within a web application (this is applicable to any web application, not just ASP.NET) because those network calls can potentially stall the threads used to process ASP.NET requests. For example, if our application gets twenty simultaneous requests and the application code that services each request makes a call to a web service, we will potentially stall and queue subsequent requests as we wait for the threads to return.

However, by using asynchronous events, you could at least free up the threads that ASP.NET isn't using for the web service calls. Let's look at a sample of this.

Sample Web Service

First, you need a sample web service. The following snippet creates a simple StockQuote Web service, in a file named StockQuote.asmx :

  <%@ WebService Class="QuoteService" %>   Imports System.Web.Services   Public Class QuoteService   <WebMethod()> Public Function GetQuotes() As QuoteDetails()   ' Create an array of 3 Quote objects for our return   Dim quotes(3) As QuoteDetails     quotes(0) = New QuoteDetails()   quotes(0).Symbol = "MSFT"   quotes(0).Price = 89.34F     quotes(1) = New QuoteDetails()   quotes(1).Symbol = "SUNW"   quotes(1).Price = 11.13F     quotes(2) = New QuoteDetails()   quotes(2).Symbol = "ORCL"   quotes(2).Price = 22.93F     Return quotes   End Function   End Class     Public Class QuoteDetails   Public Symbol As String   Public Price As Single   End Class  

This particular web service, written in Visual Basic .NET, simply returns an array of QuoteDetails with some pre- populated values. You can then write an ASP.NET application that uses this web service, and calls it asynchronously. When you build the proxy, ASP.NET automatically creates asynchronous implementations of your web service's methods.

Asynchronous Event Prototypes

Implementing asynchronous events is not trivial. Not only do you need to know whether or not the application code you're writing can benefit from asynchronous events, you also need to understand the asynchronous programming model supported by .NET.

To use these events within global.asax , you need to wire up the provided event prototype ourselves . This is done by overriding the Init method, which is marked as virtual in HttpApplication , and replacing it with your wire-up code. Unlike synchronous events, asynchronous event wire-up is not done automatically for us.

The following code, written in Visual Basic .NET, calls the QuoteService Web service asynchronously:

  <%@ Import namespace="System.Threading" %>   <%@ Import namespace="System.Text" %>     <Script runat="server">   Dim asyncResult As MyAsyncResult   Public Overrides Sub Init()   Dim beginEvent As New BeginEventHandler(AddressOf _Begin)   Dim endEvent As New EndEventHandler(AddressOf _End)   AddOnBeginRequestAsync(beginEvent, endEvent)   End Sub  

First, we override the Init method of HttpApplication so we can execute our code when global.asax is initialized . The code executed is an implementation of AddOnBeginRequestAsync . We provide this event with both a Begin and an End event handler. It is a code path within the begin event where we execute our code. The following code is an implementation for both the BeginEventHandler and the EndEventHandler :

  ' Begin Event Handler   Public Function _Begin(source As Object, e As EventArgs, _   callback As AsyncCallBack, _   extraData As Object) As IAsyncResult   asyncResult = New MyAsyncResult(Context, callback, extraData)   Return asyncResult   End Function     ' End Event Handler   Public Sub _End(ar As IAsyncResult)   End Sub  

The Begin Event Handler, _Begin , is raised when OnBeginRequest is called. Within this event handler, we create a new instance of a class called MyAsyncResult . It is within this class (defined further in the code) that we implement our code to call the web service.

The EndEventHandler , which can be used to clean up resources when the BeginEventHandler completes, is not used in this example.

Next, we find the implementation of MyAsyncResult . This class is an implementation of the IAsyncResult interface. The IAsyncResult interface is part of the asynchronous programming pattern defined by the CLR. We provide an implementation (see the product documentation for a description of the implemented properties) that contains the code we want executed:

  ' Async implementation class   Private Class MyAsyncResult   Implements IAsyncResult     Dim _asyncState As Object   Dim _callback As AsyncCallback   Dim _thread As Thread   Dim _context As HttpContext   Dim _isCompleted As Boolean = False     Public Sub New(context As HttpContext, _   callback As AsyncCallback, asyncState As Object)   _callback = callback   _asyncState = asyncState   _context = context   _thread = New Thread(New ThreadStart( _   AddressOf CallStockQuoteWebService))   _thread.Start()   End Sub     Public ReadOnly Property AsyncState As Object _   Implements IAsyncResult.AsyncState   Get   Return _asyncState   End Get   End Property     Public ReadOnly Property AsyncWaitHandle As WaitHandle _   Implements IAsyncResult.AsyncWaitHandle   Get   Return Nothing   End Get   End Property     Public ReadOnly Property CompletedSynchronously As Boolean _   Implements IAsyncResult.CompletedSynchronously   Get   Return False   End Get   End Property     Public ReadOnly Property IsCompleted As Boolean _   Implements IAsyncResult.IsCompleted   Get   Return _isCompleted   End Get   End Property  

The constructor for MyAsyncResult creates a new thread and calls the CallStockQuoteWebService on that thread. This subroutine (in the following code snippet) creates a new instance of the proxy to our QuoteService ASP.NET Web service, and uses the asynchronous implementation of the GetQuotes method. It identifies a callback, QuoteCallBack , which is called when the I/O completion port is reactivated. Within this callback we again create a new instance of the proxy class and then call its EndGetQuotes method. Finally, we save the proxy's QuoteDetails return to Application state memory so we can access it from anywhere within ASP.NET:

  Public Sub CallStockQuoteWebService()   Dim quote As New QuoteService()     quote.BeginGetQuotes(New AsyncCallback( _   AddressOf QuoteCallBack), Nothing)   End Sub     Public Sub QuoteCallBack(ar As IAsyncResult)   Dim quote As New QuoteService()   Dim d() As QuoteDetails     d = quote.EndGetQuotes(ar)   _context.Application("QuoteDetails") = d   _isCompleted = true   _callback(Me)   End Sub     End Class     </Script>  

We can now write a simple ASP.NET page that retrieves the values from Application("QuoteDetails") :

  <Script runat="server">   Dim quotes() As QuoteDetails     Public Sub Page_Load(Sender As Object, e As EventArgs)   quotes = CType(Application("QuoteDetails"), QuoteDetails())   End Sub   </Script>     <%   Dim item As QuoteDetails     For Each item in quotes   Response.Write("Symbol: " + item.Symbol + "<br>")   Response.Write("Price: " + item.Price.ToString() + "<br>")   Response.Write("<hr size=1>")   Next   %>  

Executing this code brings up the screen shown in Figure 12-15:

click to expand
Figure 12-15:

Since ASP.NET supports both synchronous and asynchronous application events, you can have more options when building your application. Coding the event to be asynchronous will free the ASP.NET worker thread to service other requests until the code executed on the asynchronous thread completes. The result is better scalability, since you're not blocking the threads ASP.NET uses to service requests.




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