In one of my previous blog posts (Writing a custom IHttpModule that handles Application_OnStart event) I've been talking about using
IHttpModule to also handle application start event which is a non-documented feature. Sure it works, but you may see some strange behaviour of duplicated (or even multiplicated) functionality being executed. You probably won't see this with your applications in development, because your local IIS isn't really heavy duty workhorse, but in production environment you may see this strange behaviour. Investigating it is even a bit more complicated because of the running application serving live traffic. Let me help you. So I will point you in the right direction and more importantly show you a solution that makes things work reliably even on a heavy load IIS.
When you create a custom
IHttpModule and put particular functionality in it that changes some static or shared resource you may see that its functionality has been run multiple times. That may seem strange since every module's initialization happens only once. At application start. Why does it seem to run multiple times then?
I've used my module to register Asp.net MVC framework to work with an existing Asp.net WebForms application. First thing that has to be done is registering routes.
RouteTable has its routes stored in a static property
Routes. That is already problematic. So registering routes multiple times will make route table X times larger, but you wouldn't see any errors, because the first set of routes will catch routes just fine so all subsequent route definitions won't get hit at all.
But routes were not the root of my invalid application execution. As mentioned I was registering Asp.net MVC in a non-MVC application. Namely Sharepoint 2010 (I know I know I'm arrogant enough to think I'm smart to do this blasphemy; I'd burn in medieval times for sure). The fact that I was also registering MVC's view engine was the problem I was hitting. View engines are also part of a static collection in the
ViewEngines class. And they work more like a queue, not as a normal collection. Don't ask me why because I haven't investigated this any further than required but that's the way it is. Details aren't really important in this case. The problem was simply that view engine was registered multiple times in various locations in queue. There's no reasonable explanation to this other than that my module's
Init() method was being called multiple times.
When I looked at
HttpApplication class' code there was just one point where modules actually got initialized. At application start. Once only. So why was it that my module got initialized multiple times or at least it seemed as it was. The reason lies in the fact that Asp.net applications are run by IIS. And as you may know for a long time, IIS has something called application pools. You probably never gave too much thought to these. All you had to actually know was that application pools are separate from each other so when one application hangs others still run as expected without interruption. Thus you separated your applications into separate pools. But you already know this, don't you? You probably do.
Now lets think of the word itself. Application pool. Yes pool. It means that a certain web application is running multiple
HttpApplication instances in one pool. Yes multiple. Otherwise it wouldn't be called a pool. »How many?« you may ask. That doesn't really matter as long as you know there could be more than one. We trust IIS to do its job. And it obviously does it so well that it made this fact completely transparent for us developers hence not many completely understand its inner workings. We rely on its robustness to provide the service. And it does. Each of these
HttpApplication instances in the pool keeps its own list of HTTP modules that it uses with each request it processes.
Now we know why our modules get initialized multiple times. Once for every
HttpApplication instance in the application pool. This is what happens on IIS when an application starts:
- when the first request comes in IIS/Asp.net creates as many
HttpApplicationinstances as it needs to process requests simultaneously. For most applications, this number is limited to the number of threads and remains in the range of 1 through 100, depending on the hardware, server load, configuration, and so on (as per Application Instances, Application Events, and Application State in ASP.NET article);
- when IIS/Asp.net creates the very first
HttpApplicationinstance it then calls its
- because this is the first instance it calls
Application_OnStart()method as well;
- it then creates as many
HttpApplicationadditional instances as it needs and calls their
- because these are subsequent instances it doesn't call their
- every call to
HttpApplication.Init()also creates HTTP module instances and calls their
Init()method right after creation;
It is now easy to understand (and it also makes perfect sense) why certain module's
Init() method gets called multiple times when application starts. For what's worth it could be called afterwards as well. I guess if IIS/Asp.net decides it needs some more
HttpApplication instances I guess it can create few more during application life. I'm not saying it does but I suppose it could.
Knowing all this information we can envision our solution that will finally make it possible to create a custom
IHttpModule that can handle
Application_OnStart event. We will do this by using static variables and thread locking. But don't worry. Thread locking only happens once so it won't cripple your application. Lets look at some code.
1: public abstract class ApplicationStartModuleBase : IHttpModule
3: #region Static privates
5: private static volatile bool applicationStarted = false;
6: private static object applicationStartLock = new object();
10: #region IHttpModule implementation
12: /// <summary>
13: /// Disposes of the resources (other than memory) used by the module that implements <see cref="T:System.Web.IHttpModule"/>.
14: /// </summary>
15: public void Dispose()
17: // dispose any resources if needed
20: /// <summary>
21: /// Initializes the specified module.
22: /// </summary>
23: /// <param name="context">The application context that instantiated and will be running this module.</param>
24: public void Init(HttpApplication context)
26: if (!applicationStarted)
28: lock (applicationStartLock)
30: if (!applicationStarted)
32: // this will run only once per application start
34: applicationStarted = true;
38: // this will run on every HttpApplication initialization in the application pool
44: /// <summary>Initializes any data/resources on application start.</summary>
45: /// <param name="context">The application context that instantiated and will be running this module.</param>
46: public virtual void OnStart(HttpApplication context)
48: // put your application start code here
51: /// <summary>Initializes any data/resources on HTTP module start.</summary>
52: /// <param name="context">The application context that instantiated and will be running this module.</param>
53: public virtual void OnInit(HttpApplication context)
55: // put your module initialization code here
Application_OnStartevent. Any other (usual) modules don't need this thread safety and checking. Unless of course required by your process.
From the additional information we have about application pools we can deduct that handling
Application_OnStart is easily possible. I've shown you a solution how to do this. But if we think of the other application event
Application_OnEnd things get a bit more dodgy. Event is executed on the last
HttpApplication just before its
Discard() method gets called. So how in the world could we detect that? I guess the best way would be to change our code and instead of using a
bool variable we could use an
int and increment it with each initialization. Then we'd be checking its value in
Discard() method and when it would get back to where it started this would mean that the last
HttpApplication is being discarded hence executing
Application_OnEnd functionality. I guess this could work but you can suggest a better solution if you know of any. The only problem could be thread locking that would happen on every module initialization to safely increment the counter. The same thing would be going on in the process of disposal. The good news is that this lock would be issued for a very very short period of time and just at application instantiation and disposal. Just long enough to increment/decrement the counter. Only the first and the last lock would take longer, because they would execute start and end event code.
If you feel like it you're more than welcome to leave a comment or at least vote on usefulness of this blog post. Click your preferred reaction below here.