Thursday, August 10, 2006

.NET 2.0 Website Conversion: AutoEventWireup Fix

[I'm going to take a short break from my family blogging and preserve for posterity my findings after converting a web project from .NET 1.1 to .NET 2.0. Pictures of adorable kids will return soon...] At work we recently converted a website from .NET 1.1 to .NET 2.0. This was accomplished by running the conversion wizard which is built in to Visual Studio 2005. After the conversion, however, we noticed that some of our pages were behaving strangely--we would have lists with duplicate items in them. This wasn't the case before the conversion, and it was my task to fix it. What I found out is that in .NET 2.0, the AutoEventWireup page directive is set to true by default. In .NET 1.1 it is set to false. According to the Common ASP.NET 2.0 Conversion Issues and Solutions document, this is a known issue due to events firing twice (Issue 23: Event handlers called multiple times). In this document it explains that the conversion wizard is smart enough to remove the duplicate event wireups, but only if they live in the InitializeComponent() method of your page's codebehind file. In our scenario, we had some event wireups in the OnInit() method (not my fault--it was inherited code!). What I didn't know what how to detect where those duplicate wireups were so I could get rid of them. So I did some more digging. As it turns out, in the online help for Visual Studio.NET 2005 there is a section which explains the AutoEventWireup page directive. (This link will only work if you have VS.NET 2005 help installed on your computer.) It explains that when the AutoEventWireup directive is set to true, ASP.NET performs a lookup using reflection to find the events in your page's codebehind class. The naming convention for the events is "Page_" + event name. So for the "Load" event, ASP.NET looks for the "Page_Load" method in your code, and calls it if it finds it. So this means that only the Page events are automatically wired up, and no others are. That makes sense, since this is a Page directive... Historically, reflection has been considered slow, or at least there's a lot of overhead when loading the assembly and picking out the parts inside of it. However, according to a comment left by Scott Guthrie on this blog:

Event wire-up is done via reflection, but the type-descriptors are cached (so the lookup is one time only).

From a performance measurement perspective, there is no difference between autoeventwireup=true/false. That is one reason we switched to it as the default with VS 2005 Web App Projects.

So now we've got AutoEventWireup set to true by default, and the wizard removed the "Page_*" methods from our InitializeComponent() methods. But we still have events firing twice. The last part of the puzzle is how to remove the remaining page events which are manually wired up. Regular expression searching to the rescue! You can use Visual Studio's "Find in Files" feature (say that 10 time really fast!). Click on Use Regular Expressions in the Find Option section and then paste this in the "Find what" text box:
\+=:b*new:b*System\.EventHandler:b*\(.*\.Page_
This will search for adding a new System.EventHandler for a Page directive, and should take care of most variations in using white space. It will also handle the "this" keyword is in front of the delegate name. I hope this helps someone else out there understand how AutoEventWireups work, and how to fix your web site if you run into the same situation as I did. Please leave a comment if it helped. Thanks! [Update: You'll also want to check to see if the AutoEventWireup @Page attribute is explicitly set to false before you remove the wireup from the InitializeComponent() method, or else the page event won't fire!]

No comments: