Thanks to everyone who came to my debugging presentation at VSLive! in Orlando. I received some nice compliments afterwards that mean a lot to me.
Thanks to Scott C. Reynolds for hanging at the exhibition dinner.
Thanks to Sam Gentile and Robert Hurlbut for letting me tag along to the seafood dinner on Wednesday. Robert will be coming to the MAD Code Camp in a few weeks. Sam will be coming to my area in December to speak at the CMAP user group. Sam was recently elevated to the status of Architect MVP, so I expect him to trade in his Rotor and IL slides for clouds and class diagrams.
Finally, thanks to Ken the van driver who took me back to the airport. Ken was the most entertaining and informative driver I’ve had the pleasure of meeting. Have fun on your vacation and upcoming retirement, Ken.
Hidden in the numerous debugging features of Visual Studio 2005 is an XSLT debugger.
Open an XSLT file in the IDE and the XML menu and toolbar will both appear with “Debug XSLT” options. Selecting debug will prompt you for an XML file to transform. The output appears in real time.
What is amazing about the debugger is the number of classic debugger features that “just work”. You can set break points, step in, step over, and run to cursor. The locals and watch windows work, as does the call stack window (you can see which apply-templates brought you to the current location). Another goodie: the immediate window lets you type in XPATH expressions for evaluation in the current context.
Wait, there’s more!
2005 can also step into the Transform method of an XslCompiledTransform instance. Just enable debugging by passing a true as the first parameter for the XslCompiledTransform constructor. Be sure to read the remarks section of the documentation because some restrictions apply. In one scenario I can consistently crash the release candidate build of 2005.
When I did the Atlas hands on lab weeks ago, the following piece of code jumped out at me:
int index = Array.BinarySearch(
autoCompleteWordList,
prefixText,
new CaseInsensitiveComparer()
);
if (index < 0)
{
index = ~index;
}
Why is the code doing a bitwise complement (~) on the return value of BinarySearch?
My next stop was the documentation for the return value of Array.BinarySearch, which states:
The index of the specified value in the specified array, if value is found. If value is not found and value is less than one or more elements in array, a negative number which is the bitwise complement of the index of the first element that is larger than value
In other words, if you don’t find an exact match you can flip all the bits and have an index pointing to something close – a good trick to know when building an auto-complete control.
One of the advantages to ASP.NET server controls is the ability to package them into an assembly and reference them from other web applications. Server controls are relatively difficult to write but easy to reuse. User controls (ascx files), on the other hand, are relatively easy to develop, but don't like to swing with other projects. A common solution in 1.x involves setting up virtual directories. Yuck.
The ASP.NET 2.0 environment is different. We have MSBuild. We have an ASP.NET compiler. Perhaps you've also noticed we have a tool by the name of ILMerge.
Here is a proof of concept.
Step 1: I created a new solution in Visual Studio 2005 and added a plain class library project. I then added two user controls to the project, which Visual Studio doesn’t like initially, but it does all work, even the intellisense. The first user control is all inline code:
<%@ Control Language="C#" ClassName="SayHello" > <script runat="server"> <protected void Page_Load(object sender, EventArgs e) { label.Text = "Hello at " DateTime.Now.ToShortTimeString(); } </script> <asp:Label runat="server" ID="label"/>
The second control, SayGoodbye.ascx, is the same, except it puts the Page_Load logic into a separate CodeFile by the name of SayGoodbye.cs.
Step 2: One of the cool features of Visual Studio 2005 is that the project files are MSBuild files. The default .csproj file for the class library I created does not know what to do with .ascx user control files, but I help it. I can right-click the project and select unload, then right-click the now disabled project and select Edit. Behold - the project is exposed naked before me. Pure angle-brackety goodness.
What I want to do at this point is modify the project to run the ASP.NET precompilation task – easy enough since there is an MSBuild task available by the name of AspNetCompiler. What is tricky is that AspNetCompiler will likely produce multiple assemblies. The compilation tool will batch compile by default, which means one assembly per directory of user control files. Of course we don’t want to keep all the ascx files in the root of the project, and we don't want to reference an entire directory of .dll files, so this is where ILMerge comes in.
ILMerge is a utility to merge multiple .NET assemblies into a single assembly. Combining AspNetCompiler and ILMerge together gives us something like the following:
<Project DefaultTargets="CompileUserControls" xmlns="http://schemas.microsoft.com/developer/msbuild/2003"> ... <Target Name="CompileUserControls"> <!-- TargetPath cannot be underneath PhysicalPath :( --> <AspNetCompiler Debug="false" PhysicalPath="$(MSBuildProjectDirectory)" TargetPath="$(TempDirectory)" Updateable="false" Force="true" VirtualPath="$(MSBuildProjectName)" /> <CreateItem Include="$(TempDirectory)\bin\*.dll"> <Output ItemName="PrecompiledAssemblies" TaskParameter="Include" /> </CreateItem> <Exec Command="$(ILMergeEXE) /out:$(MSBuildProjectName).dll /targetplatform:v2 @(PrecompiledAssemblies, ' ')" /> </Target> ...
Inside the target I first run the AspNetCompiler and produce all the user control assemblies. Next, I need a list of all the assemblies the compiler just spit out. I do this with
Plop! Out comes ReusableControls.dll.
For the last step, I created a new web project and referenced ReusableControls.dll, then created a little test aspx web form.
<%@ Page Language="C#" %> <%@ Register TagPrefix="rc" Namespace="ASP" Assembly="ReusableControls" %> <html xmlns="http://www.w3.org/1999/xhtml"> <form id="form1" runat="server">
<rc:SayHello runat="server" ID="hello" />
<br />
<rc:saygoodbye_ascx runat="server" ID="goodbye" /> <br /> </form>
Notice in the @ Register directive we need to specify a namespace of ASP, as this is the default given by the AspNetCompiler. The compiler also munges the user control name when a CodeFile is used – we are using the class that was code-generated from the SayGoodbye.ascx file and inherits from the SayGoodbye class in the CodeFile. With inline code there is no adjustment.
It might be possible to also re-use master pages and webforms with a little bit of VirtualPathProvider trickery, but I’m not going there as yet. I want to see how this will shake out at RTM time when combined with the Build Project that should arrive, as announced by others.
ASP.NET developers typically handle a Page object’s Load event with a Page_Load event handler. In 1.x the designer generates code inside an InitializeComponent method to explicitly wire Page_Load to the Load event.
Lately, I’ve seen more than a handful of people dropping the Page_Load event handler in favor of overriding the Page OnLoad method. This appears to be happening more and more in 2.0, for a few reasons.
First, for C# developers using a separate CodeFile, it’s not immediately obvious how to add any Page event handler if the event is not already present. Unlike the VB editor, there are no drop down controls available to add an event, and intellisense offers no assistance. The method signature has to exactly match what ASP.NET will be looking for, because C# webforms use AutoEventWireup=”true”. Ironically, VB webforms use AutoEventWireup=”False” because the Handles keyword allows a VB developer to wire up an event in place.
Secondly, there is a small performance benefit in not creating a delegate (with reflection no less) and having the garbage collector clean up the same delegate afterwards.
I’ve been thinking about the OnLoad approach, and I even posted code in a recent entry using OnLoad instead of Page_Load. I’m not comfortable enough to make the switch, however. The purpose of OnLoad is to raise a Load event. Overriding a virtual method implies I want to change that behavior, which isn’t precisely true – I just want to setup the webform and get ready for databinding, etc. There is also a comfortable consistency in handling all Page and control events using event handlers of the form VariableName_EventName. Finally, if someone forgets to forward the OnLoad call to the base class, frameworks will break easily.
Opinions?
P.S. To add Page event handlers in C# code easily, use inline code instead of a separate code file. The ASPX editor provides the drop down controls to select Page events when in source view. Drag and drop, baby. It’s the future.
A Few Good Objects
Col. Jessup: You want destructors?
Kaffe: I think I’m entitled.
Col. Jessup: You want destructors?
Kaffe: I want deterministic finalization.
Col Jessup: You can’t handle deterministic finalization!
Pulp Compilers
Vincent: You know what they call a switch statement in VB?
Jules: They don’t call it a switch statement?
Vincent: No man, this is the same language that has an AndAlso operator.
Jules: Then what do they call it?
Vincent: They call it a Select Case.
Full Metal Packet
Gunnery Sargent Hartman: My orders are to weed out all non-hackers who do not pack the chops to serve on my beloved accounting package software project. Do you maggots understand that?
Star Warez
[R2-D2 and Chewbacca are pair programming aboard the Millennium Falcon]
Chewbacca: Wurgleaaaaaaaghooooo!
C-3PO: It’s legal syntax. Screaming about it won’t help you.
Han Solo: Delete it. It’s not wise to upset a wookie.
C-3PO: But sir, nobody worries about upsetting a droid.
Han Solo: That’s because droids don’t pull people’s arms out of their sockets over bad code. Wookies are known to do that.
Chewbacca: Rrrrroo.
C-3PO: I see your point, sir. I suggest a new strategy , R2. Let the wookie take the keyboard.
Lord Of The Token Rings
Aragorn: Are you frightened?
Frodo: Yes.
Aragorn: Not nearly frightened enough. I know your bug count.
Null Terminator 2
Terminator: I need your source code, your compiler, and your static code analysis tools.
Biker: You forgot to say please…
Bridge Pattern Over The River Kwai
Colnel Saito: Do not speak to me of rules! This is software programming! This is not a game of cricket!
2001: The Speech Recognition Odyssey
Dave: Open the pod bay doors, HAL.
HAL: What did you say about Bombay, Dave?
Dave: I said open the pod bay doors, HAL.
HAL: I don’t know anyone named Doris, Dave.
Dave: Give me a freakin’ keyboard!
Here is a sampling of pictures I took during the summit.
This first photo is from Wednesday night’s dinner. Roughly 35% of the seats at my table were filled by a Scott. In this photo, Scott Hanselman is comparing mobile devices as Scott Bellware and Milan Negovan look on. Scott H does a great Jimmy Stewart.
Also at the table was Bill Vaughn. Bill and I suspect we are long lost cousins – we both descend from Allen families of the Shenandoah Valley.
Here is a shot of Bill Ryan, Sahil Malik, and Jonathan Cogley. Sahil is presenting at the local user group this Tuesday – come on out and ask him why he prefers a Motel 6 to the W. Whatever, Sahil.
This is a picture of my favorite bus driver: Ted. Ted is what I call an optimistic driver. When Ted would see cars stopped 100 feet ahead on the freeway, Ted wouldn’t slow down a bit. Ted believed the cars would be moving again by the time we arrived at their bumper. We often decelerated from cruising speed to a dead stop in 3 milliseconds. In spite of passengers screaming “Ouch, my spleen!”, Ted remained very optimistic.
I have pictures of G. Andrew Duthie on a Karaoke stage, but Paul Glavich has done one better and posted video. We need a Karaoke machine at the next code camp.
This last photo is Darrell Norton at the airport. Darrell, in true agile form, was demonstrating user stories in preparation for project “Getting Some Sleep On A Redeye Flight”.