Saturday, January 13, 2007

Session_Start or Session_OnStart?

Ever since I started to develop with ASP.NET, I’ve been wondering why global.asa from the ASP days quietly moved over to ASP.NET as global.asax. When you look at it, it just feels so outdated, so VBScript-ish, so loosely–typed. And what is the right way to name event handlers?

Even MSDN documentation is contradictory on this. For example, which is a correct handler for the Session.Start event? Session_Start or Session_OnStart? Documentation lists both in different places, and when you look it up newsgroups, you’ll read some pretty opinionated arguments about each.

For example, MSDN states:

You can use the Global.asax file to synchronize any event that is exposed by the HttpApplication base class. To do this, you must use the following naming pattern to author methods:

Application_EventName(AppropriateEventArgumentSignature)

According to Framework Design Guidelines, section 5.4.1, Custom event handler design, an “appropriate event argument signature” is one with a return type of void; object as the type of the first parameter of the event handler, and called sender; System.EventArgs or its subclass as the type of the second parameter of the event handler, called e.

Nevertheless, you see the following code snippet time and time again:

void Session_OnStart() {
// Session start-up code goes here.
}

void Session_OnEnd() {
// Session clean-up code goes here.
}

First, what happened to an appropriate signature? Second, the Session_End event is so flaky that you shouldn’t bank on it.

Here’s another quote from Handling Public Events:

In any of these classes, look for the public events they define. You can hook them in Global.asax using the syntax ModuleName_OnEventName. For example, the Start event of the SessionStateModule module is declared protected void Session_Start(Object sender, EventArgs e) in Global.asax. You can use the syntax Session_OnStart in a <script runat="server"></script> block in an ASP.NET application file to define an event handler for the Start event of the SessionStateModule.

So do you need that On prefix or not? And should handlers be private, protected or public? No wonder there’s so much confusion over this.

Quiz

How about a quiz? What if you define the following methods in global.asax (yes, all of them):

  • void Session_Start(object sender, EventArgs e)
  • void Session_Start()
  • void Session_OnStart(object sender, EventArgs e)
  • void Session_OnStart()
  • void Session_Start(object sender)

Having these five event handlers, which one(s) will be called? Place your bets.

And the Winner Is…

It turns out all of them, except the last one, will be called, and in the same order as listed! Not that anyone would want to declare more than one, but it demonstrates that the On prefix does not matter. Neither does it the access modifier—private, protected or public—matter. As long as the handler has no arguments, or has two arguments that resemble a correct signature, it will be called.

Digging Deeper

I got curious why this was taking place and started digging with Reflector. Eventually, I found a method, HookupEventHandlersForAppplicationAndModules, inside the HttpApplication class. I believe this is where event handlers of modules are magically wired.

This method goes through a list of methods that look like event handlers, extracts the part before the underscore (e.g. “Session” in Session_OnStart), and looks up an HttpModule with this name. It then extracts the part after the underscore (taking into account the optional On prefix), and then creates a delegate with the extracted name. Next, the method tries to add this delegate to the right event in the identified module. If an event handler had no parameters, a special ArglessEventHandlerProxy comes to the rescue.

Which Methods Are Picked Up?

The only missing bit is which methods in global.asax look like event handlers. You can see the algorithm in the ReflectOnMethodInfoIfItLooksLikeEventHandler method of HttpApplicationFactory. In a nutshell, this method checks if the method has a return type of void; and if the first argument to a method is of type object and the second—of type EventArgs or a derivative thereof. If the signature is satisfactory, the method is passed on as a possible event handler. A special case is a method without parameters—it’s treated as a potential event handler as well.

Conclusion

I hope at this point it is clear why all variations of Session.Start event handlers in global.asax, except the last one listed, were called. I still don’t understand the reason for all this late binding and the need to drag this file around. At least this article should settle disputes about the “proper” naming of event handlers within global.asax.