by Jeff Cogswell

Sometimes, when developing Web applications with ASP.NET, you encounter a problem like this. You have a small amount of data that rarely changes; you need that data again and again throughout the life of the application; and further, such data is shared among users accessing your application.

For example, you might have an online sales application that serves a limited region. Your application is aware of 50 different zip codes. For each zip code, you might associate the phone number of the sales office serving that zip code. This data might be used in various places in your application, including as a datasource for some Web server controls.

You could easily store this data in a database, such as Oracle or SQLServer. But you probably don’t want to retrieve the data from the database every time you use it, even if you allow for caching on the server. I’ve found a better approach for such situations.

Initially, I store the data in a database. When the application starts up, I load the data into a single instance of a container, and then use that container whenever I need to access the data. This requires that you mind your Ps, your Qs, and your SIs (that is, static initializers). It also requires that you have a good understanding of how applications are started and stopped on the Microsoft IIS server, which runs the ASP.NET framework. In the rest of this article, I dive into these different topics to help you make sure your static data is well cared for.

Application Lifecycle

One big difference between an Internet application and a traditional client-side application is that, with an Internet app, each user doesn’t get his own copy of the program running on the Web server. In addition, users have little control over the starting and stopping of the application. For you, as a developer of such applications, this can be a major pain in your exit ramp if you’re trying to use code that relies on the application start-up, unless you know what you’re doing. That’s why a good understanding of the ASP.NET application lifecycle is necessary.

Note: The Web server does not run a separate copy of your application for each user accessing your web site. Bear this in mind as you read through the rest of this article, because you don’t want to code global static variables as if each user has a separate copy of them. For per-user data, use session variables instead. For information on session variables, search the MSDN online help for HttpSessionState.

When you install your application on a Web server, it waits for somebody to access it. For example, suppose you’re the Great Owner of the Microsoft.com Web site, and you install an application called superapp, which people access through the URL http://www.microsoft.com/superapp/default.aspx.

You went through the motions of configuring the application on the IIS server. But now it just sits there, waiting for the first user to come. When that finally happens, the IIS server goes through a bunch of rigmarole to initialize your application.

Now your application is running. And the user does his or her business (which is hopefully profiting you, or, in the very least, Bill Gates, since he needs the money). Then the user goes on her merry way. But what about the application? It’s still running. Two minutes later, another user comes along to the same URL. Your application is still running, ready to serve up the Web pages.

When does it end? If the application goes idle for a certain period of time, the ASP.NET runtime will end your application for you. But if people are still connecting to the Web site, the application will still be running. Only after all is quiet will the thing finally shut down (assuming it doesn’t crash, of course).

The whole process is somewhat complex (see the online help for the HttpApplication class for a list of the steps that take place; also check out Microsoft Knowledgebase article number 312607 for the whole details). But the important thing to worry about here is that when your application starts up, the ASP.NET framework creates an instance of HttpApplication for you. Or — and I’ll give the details to this shortly — you can create your own class derived from HttpApplication, where you can store your global data (such as the zip code idea I mentioned earlier) inside the Global.asax file.

However, what I just said isn’t the full story. You don’t get just one HttpApplication. That’s where it gets bizarre. The ASP.NET system actually creates multiple instances of HttpApplication (or of your derived class in Global.asax) in order to handle multiple requests. The whole idea is so that IIS and the ASP.NET system can handle high-performance Web sites that get heavy traffic.

Even though there are multiple HttpApplication instances, there’s still no guarantee which of these are shared among which users. Each user accessing the site does not necessarily get his own instance of HttpApplication. In general, you don’t want to make any assumptions about the individual instances of HttpApplication. And remember, even though there may be multiple HttpApplication instances, only one instance of the application itself is running on the server.

Your Data and the HttpApplication Class

Now, back to our nice little example about zip codes. Where should you store this data? If you’ve done traditional, non-Web programming, you probably created global classes to hold your data. In such classes, you typically only want one instance of your global data; so, you either make the data static (or shared in the VB language), or you use a singleton class, which is accessible through a static member function. When the application starts up, you initialize your global data, and go to town.

This same technique works in Web applications. You can create one set of your global data that gets loaded when the application starts. The only catch is that you need to recognize that you don’t know when your application will start up and shut down, and you don’t know how many users will be using it. But just to stir up the mud a bit, remember also that you don’t know how many instances of HttpApplication you’ll get.

One good (and Microsoft-approved) place to put your data is in your own application class you derive from HttpApplication. This class, as I mentioned earlier, is in your Global.asax file. However, just as in your traditional programs, youshould make the data static (or shared). Why is that? Remember that you might get multiple instances of HttpApplication (or some derived class thereof). Since you only want one set of your global data, if you make it static (or shared) you’ll get only one set shared among these instances of HttpApplication, and ultimately among all users.

Following is an example, but first I need to make a quick comment, because I like to talk a lot and point out annoying little things. ASP.NET and ASP.NET 2.0 have two different ways that you derive a class HttpApplication. The old way involved creating a class that was somewhat cumbersome. The new way with 2.0 is much simpler; that’s the approach I give in this example, which means this will work on the 2005 versions of the compilers. Here’s a Global.asax file:

<%@ Application Language="VB" %>

<script runat="server">

    Public Shared GlobalNumber As Int32
    Sub Application_Start(ByVal sender As Object, _
    ByVal e As EventArgs)
        GlobalNumber = 100
        Logger.WriteLine("Application_Start")
    End Sub

</script>

Although not obvious from the code, this code creates a class derived from HttpApplication called Global_asax. The namespace for this class is ASP. The code I’m showing you here is an event handler called Application_Start, which is called when the application starts up. In this code, I initialize a shared/static variable called GlobalNumber to the value 100. Thus, I have my own static global variable.

Incidentally, if you want to access the Global_asax class from elsewhere you would do the following, shown in bold:

Label1.Text = ASP.Global_asax.GlobalNumber.ToString()

This line would be in an aspx.vb file, and I’m assuming you have a Label control called Label1. The line assigns the value of the static global GlobalNumber to the label control (by calling ToString to appropriately convert the number to a string).

Just to see what’s going on, I made use of a handy little class I wrote called Logger, which I make use of in the code; the code calls Logger.WriteLine and logs a message just so I know the application started up. Yes, there are various trace utilities and such available, but I find this one serves my purposes pretty well. You’re free to use it all you want.

Note, however, that this class is not thread-safe, and it’s just for some quick debugging. Please don’t use it for your production code and if you do, use it at your own risk. Here it is if you want it; note that you’ll have to create a directory called log off the root of your application’s directory, and you need to give the Web server write-access to that directory.

Imports System.IO

Public Class Logger
    Public Shared Sub WriteLine(ByVal Line As String)
        Try
            If Not Inst.LogFile Is Nothing Then
                Inst.LogFile.WriteLine(Line)
            End If
        Catch ex As Exception
            ' If the logger blows up, I'd rather
            ' have it just do nothing
        End Try
    End Sub

    Private Shared Inst As Logger = New Logger
    Private LogFile As StreamWriter
    Private Sub New()
        Dim filename As String
        Try
            filename = System.Web.HttpRuntime _
                .AppDomainAppPath _
                & "\log\BDLog" & DateTime.Now.ToString( _
                "yyMMdd-HHmmss") & ".log"
        Catch ex As Exception
            ' In case we're running locally...
            filename = "c:\temp\BDLog" & _
                DateTime.Now.ToString( _
                "yyMMdd-HHmmss") & ".log"
        End Try
        LogFile = File.CreateText(filename)
        LogFile.AutoFlush = True
        LogFile.WriteLine("testing...")
    End Sub

    Protected Overrides Sub Finalize()
        Try
            If Not LogFile Is Nothing Then
                LogFile.Close()
            End If
        Catch ex As Exception

        End Try
    End Sub
End Class

This class initially opens up a file in the log directory, with a name based on the current time. The class also provides a WriteLine member function that writes a string to the file. (It’s not overloaded, so all you get are strings. You can cast your data before calling WriteLine or add your own overloaded methods.)

But In Actuality…

Now, in truth, you don’t have to use a class derived from HttpApplication to store your static data. And frankly, I don’t like stuffing all my data into a single class, such as one derived from HttpApplication. A good way to accomplish this is to make use of the Application_Start event, to initialize the singleton class. You create a singleton class to hold your global data; then, inside this event, add a call to initialize your global data. Here’s an example. First, here’s the singleton class, which you would put in its own file, such as Singleton1.vb:

Public Class Singleton1
    Protected Shared Inst As Singleton1
    Public Shared Function GetInst() As Singleton1
        Return Inst
    End Function

    Public Shared Sub Setup()
        Inst = New Singleton1
    End Sub

    Protected Sub New()
        pHeight = 20
        pWidth = 30
    End Sub

    Dim pHeight As Int32
    Dim pWidth As Int32
    Public ReadOnly Property Height() As Int32
        Get
            Return pHeight
        End Get
    End Property
    Public ReadOnly Property Width() As Int32
        Get
            Return pWidth
        End Get
    End Property
End Class

This code includes a shared Setup function that creates the singleton instance, storing the instance in the shared but protected Inst member. One vital aspect to this class is that the constructor (the New subroutine) is protected. We don’t want code trying to create its own separate instances of this class; making the constructor protected prevents anyone from manually creating instances.

Instead, this code provides an “official” method for obtaining an instance, and that’s the GetInstance method. The method checks if an instance exists, and if so, returns that instance; otherwise, it creates a new instance and returns it. Also, since this is global data we’re dealing with, we don’t want someone else’s code going on a field day and modifying the data. (This is true, of course, if you work on a team and your teammates are less than competent. Even if they’re good, this is still good coding practice.) So to allow for accessing the data, I provided member functions. (Or you can just use property accessors that return the data if there’s nothing to pass; the choice is yours, so long as you don’t give the users of the class a means of modifying the data.)

And now here’s a sample Global.asax that calls the singleton class’s Setup() function to get the ball rolling:

<%@ Application Language="VB" %>

<script runat="server">

    Public Shared GlobalNumber As Int32
    Sub Application_Start(ByVal sender As Object, _
    ByVal e As EventArgs)
        Singleton1.Setup()
        Logger.WriteLine("Application_Start")
    End Sub

</script>

And finally, here’s another sample call making use of the singleton class. This one accesses one of the properties, Width:

Label1.Text = Singleton1.GetInst().Width.ToString()

The Singleton Classes Tangent

If you’re unfamiliar with the concept of design patterns, here’s a quick introduction. A design pattern is a concept that dates back into the world of architecture (as in buildings, those great big things we walk around in and ride elevators in). In the computer world, a design pattern is a type of class that you create over and over.

A singleton class is an example of a pattern. Often, you want to build a class that allows only a single instance of itself, thus the name singleton. In a nutshell, the way to do this is to block access to your constructor so that users of the class can’t directly create instances of the class. You do that by making the constructor protected or private. Next, provide a static (or shared) member variable that contains the single instance (or in languages like C++, a pointer to the instance). Make this member protected or private as well, to block direct access so that users can’t just rewrite the value with a null value or something. Finally, provide a public static member function that checks if the single instance exists, and if not, creates it; then, the function returns the instance. This function provides the user with access to the single instance of the class. That’s it! You have a singleton class.

What About Regular Statics?

As with any static data, if you code your classes correctly, you can swing it so your statics throughout your program get created when the application starts, even those outside of HttpApplication.

In this case, such static data is initialized each time the ASP.NET runtime starts up your application.

I’ve used this technique myself, but be forewarned: this is not the official Microsoft-approved way of creating static data, so as usual, use at your own risk.

One way to accomplish such static initialization is to throw a line in your class, like the one below, shown in bold.

In this code, the Inst member is initialized when the application starts up (at least technically speaking; I say more about this shortly). Then you don’t need a Setup function, and you don’t need to do anything inside your Global.asax file. You can simply access the data in the same way as before, through the GetInst() static function.

Public Class Globs1
    Protected Shared Inst As Globs1 = New Globs1
    Public Shared Function GetInst() As Globs1
        Return Inst
    End Function
    Public Function SomeValue() As Int32
        Return 10
    End Function
    Protected Sub New()
        Logger.WriteLine("Startup")
    End Sub
End Class

If you test out this code by putting a breakpoint on the Logger line, you’ll find ASP.NET is actually quite smart and the object might not get created until it’s actually used. Either way, it works.

This approach might look slightly familiar, if you looked over the Logger class I provided earlier. That class uses this same technique. The class includes a static initializer that creates the single instance of the Logger class. But in this case, I don’t provide any public member functions, other than a single shared function, WriteLine. Since this function is shared, I saw no reason to provide a function for accessing the singleton instance, either. Instead, the function accesses the singleton instance itself.

With this approach, however, note that I offer a very big caution. Because you don’t have control over exactly when your instance is created, you also can’t be sure that all the intrinsic objects were created. (By intrinsic objects, I mean things like the HttpContext instance, for example.) In general, I’ve found that such objects are available by the time my static object is created by this method; however, Microsoft warns against such things, and we had better listen. Thus, for important production or mission-critical code (unlike my Logger class), I always make sure I include a Setup function that I call from the Application_Start event. But for smaller applications where I’m not as worried, I use this approach, and I have never had a problem.

What if you want to allow code in your program to be allowed to modify the global data? Yuck.

First, let me say this: Consider redesigning your code. Global data shouldn’t be a free-for-all. But if you insist (since there are, of course, some valid times), you should lock the data before you write to it. ASP.NET runs in a multithreaded universe, and if two threads try to modify the data simultaneously, the whole multithreaded universe will explode. (Or at the very least, the data will become corrupt and unstable.) To learn about locking, look in the online help for the HttpApplicationState.Lock method. Oh and remember, locking like this can cause your application to slow down a great deal, if you have to do a lot of locking. You’ve been warned.

A Singleton-on-Demand Class

The previous example creates the instance at some time before you need it, but it’s not totally clear when the runtime system gets around to making the instance. So how about a one-better class that initializes the data the first time you need it? Here’s one an example:

Public Class Singleton2
    Protected Shared Inst As Singleton2 = Nothing
    Public Shared Function GetInst() As Singleton2
        If Inst Is Nothing Then
            Inst = New Singleton2
        End If
        Return Inst
    End Function

    Protected Sub New()
        pHeight = 20
        pWidth = 30
    End Sub

    Dim pHeight As Int32
    Dim pWidth As Int32
    Public ReadOnly Property Height() As Int32
        Get
            Return pHeight
        End Get
    End Property
    Public ReadOnly Property Width() As Int32
        Get
            Return pWidth
        End Get
    End Property
End Class

This class is almost exactly the same as the previous Singleton1 class, with two notable exceptions, shown in bold. First, I added some lines to the GetInst function that look for the existence of the single instance, and if the instance doesn’t exist, create it. Next, to cover my beehive, I initialized the singleton instance, Inst, to Nothing, just to be extra-guar-own-teed that the test if Inst is Nothing will succeed the first time. Oh yes, and I also get rid of the Setup function and removed its call from the Global.asax file.

Now if you’ve been around the block a few times, you might recognize this code as the correct way to implement a singleton pattern, without any undue influence from the aggressive ASP.NET runtime — except for the locking mechanism I put in place just to be sure nothing gets trampled on in the even two threads end up calling GetInst at precisely the same time. Also, this code doesn’t suffer from the same problem as the previous example, in which you can’t be sure the intrinsic variables all exist. The reason is I make sure that I only call GetInst from within my forms and from within code called by my forms, at which point the intrinsic variables all definitely exist.

What does all this mean? Global singleton instances work in ASP.NET just as you would expect and hope they do. However, remember that your application might unload and get reloaded at any time; don’t use this code to create an instance of something that expects to be up and running all the time. And make sure you write code that is okay shutting down and restarting from time to time. (But shouldn’t all programs meet that requirement, anyway?)

And finally, remember once again that your application doesn’t load separate copies for every single user. That means each user doesn’t get a set of global static variables. The variables are shared among all users as long as the application is running.

But Wait! What About…

If you’ve done some exploring of ASP.NET, and especially of the earlier pre-.NET days of ASP, you might wonder why you don’t just store your global data in the Application list, which is well-documented. Well, you can. Inside the code for a form you can use the intrinsic Application variable like so:

Application("appname") = "CoolApp"

Or, outside form code, you can access the Application variable through the HttpContext class, like this:

HttpContext.Current.Application("appname") _
= "CoolApp"

However, Microsoft recommends against this practice. The only real reason this feature exists is for backwards compatibility with old ASP programs. But why not use it? One big reason is speed. If you’ve got a high-traffic Web site, and you’re doing a lot of lookups to the Application object, you need to seriously concern yourself with every little thing that takes up time, because they all add up. And accessing your static data directly is much faster than trying to access the data through a hash array, such as the Application object.

Plus, since the Application object is for backwards compatibility, we have no guarantee Microsoft will keep it in every future version of ASP.NET. It’s best to keep up with the times.

Another Tangent: Manually Restarting Your Application

When you’re developing your application and dealing with static data, you probably need to restart the application several times, so you can actually get your changes in and test things out. If you need to restart your application, there are a few things you can do.

In theory, you should only have to rebuild your application for ASP.NET to restart it. However, I’ve found doing so to be unreliable, especially with ASP.NET 2.0, where your individual aspx files get compiled into separate assemblies; sometimes the system doesn’t automatically restart the application. I’ve had times where I’ve modified a class that’s initialized in the Application_Start event, and when I rebuilt and run the application, I’ve received a whole bunch of null pointer exception errors, because for whatever reason the Application_Start even didn’t actually occur yet. Thus, I’ve had to force the reload of my application.

One quick way to force the application to reload is to “touch” the Global.asax file. (You do have a Touch command on your computer, right? That’s a program that does nothing more than update the date and time of a given file to the current date and time. It’s a very useful tool for developers, and you can do a Web search to find one. Or heck, just write one yourself if you can’t find one.)

Here’s another method. Okay, maybe I’m a bit of a brute at times when dealing with this amazingly intricate yet delicate system based around an expensive, high-tech central processing unit, but this is a quick and dirty way to kill my application. (And no, I’m not going to say throw the computer out the window, even though sometimes I feel that way.) I just open up the Task Manager, find the aspnet_wp.exe name in the Processes tree, and click End Process. IIS immediately restarts aspnet_wp.exe with a clean start. Yeah, yeah, that’s dirty, as it doesn’t let your application do any proper shutdown and cleanup. But on my own development box, it works just fine, and it’s quick. (But use this method at your own risk. Don’t sue me if you lose all your sales data doing this.)

No Static Here

As with all things Microsoft, you have to be very aware of the implications of seemingly small tasks, such as creating static data. With ASP.NET, you especially have to be aware of everything going on behind the scenes, considering that you will probably have multiple people accessing your application, and ASP.NET is a rather sophisticated (read: complex) system.

As I tell my students and coworkers, study up on the documentation and learn what’s going on under the hood. Static initialization is no exception. Mind your Ps and Qs, and you’ll get it working well, and without bugs.

Copyright © 2005 Ziff Davis Media Inc. All Rights Reserved. Originally appearing in Dev Source.