Understanding the scope of the Application collection for a given application is important. As mentioned above, the Application object is created the first time a client requests a URL within the application. The boundary of that ASP.NET application is defined by the boundary of an IIS application; the boundary of the ASP.NET application includes all of the ASP.NET pages within a single IIS Application and all of its subfolders and virtual directories. It does not, however, include any subfolders that are defined as IIS Applications. Figure 13-1 illustrates the different folder types in IIS. In Figure 13-1, the SubApp subfolder is a child folder of the Chapter_13 application and shares Application state with it. If, however, the SubApp folder is configured as an IIS Application (by accessing the Virtual Directory tab of the Properties dialog for the folder and clicking the Create button in the Application Settings section), it will then define its own application boundaries, which will not be shared with the parent Chapter_ 13 application.
In classic ASP, a big no-no was storing non-thread-safe COM objects (which meant any COM object written in Visual Basic) in the Application collection. This was because such components would force IIS to process requests to the Application that stored the COM object only from the same thread that created the object, which substantially limited scalability. In ASP.NET, this is less of an issue, since all managed .NET components can be stored safely in the Application collection without having an impact on scalability due to threading model considerations.
When accessing any resource potentially shared by many clients, one concern is the synchronization of access to that resource. For instance, imagine that you declare a variable in the application collection to track the number of users currently logged into your application. To do so, in your Session start event handler (also defined in the global.asax file and covered completely in Chapter 19), you can place code such as this:
LocalVal = Application("Counter") Application("Counter") = localVal + 1
This contrived example (don't use it in your application) shows a problem that can occur with global variables shared by multiple threads. Imagine that two sessions are created simultaneously. Each session gets its local copy of the Application variable, which will be the same since they were requested simultaneously. Then each thread sets the Application variable to its own local value plus one. In the end, though two clients incremented the value of our counter, the value will increase by only one.
Fortunately, a solution is provided within the HttpApplicationState class. The Lock and UnLock methods allow a developer to synchronize access to shared resources. Lock and UnLock must be called in matched pairs. The Lock method is entered only when no other client executes code between calls to Lock and UnLock. Win32 programmers may recognize the similarity between the use of Lock and Unlock in the HttpApplicationState class and Win32 critical sections.
The example above could be rewritten to be thread safe as follows:
Application.Lock( ) LocalVal = Application("Counter") Application("Counter") = LocalVal + 1 Application.UnLock( )
While using Lock and UnLock solves the problem, their use should be absolutely essential. For instance, synchronization of Application-level variables is not required in the Application start or Application end event, since these events are called only once—just before the first client operation starts and just after the last client operation ends, respectively. Excessive use of Lock and UnLock can degrade both performance and scalability.
In addition to properties and methods provided for backwards compatibility with classic ASP, the ASP.NET version of the Application object adds several useful new properties and methods, including the AllKeys collection, the Count property, and the Clear, Get, GetKey, RemoveAll, RemoveAt, and Set methods.
In this chapter, we'll use the following code listing as the basis for most examples in the chapter. Unless otherwise noted, each example consists of just the Page_Load event handler for that particular example. Any output messages or return values displayed are shown as the Text property of the ASP.NET Label control named Message or displayed by calling Response.Write.
<%@ Page Language="vb" %> <html> <head> <script runat="server"> Sub Page_Load( ) 'Example code will go here End Sub </script> </head> <body> <asp:label id="Message" runat="server"/> </body> </html>