Professional ASP.NET 1.1 [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Professional ASP.NET 1.1 [Electronic resources] - نسخه متنی

Alex Homeret

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید






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:


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:


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:


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.

/ 244