February 2006 - Posts

Caveat With ASP.NET Precompilation and web.config Settings

When ASP.NET 2.0 compiles a web site, it reads the site’s web.config to pick up a few settings that affect the generated code. For instance, ASP.NET reads the <profile> section to generate the code for a Profile class with strongly typed properties.

What may not be intuitive at first is what happens with these web.config settings when the ASP.NET precompilation tool creates a “non-updateable” web site (a full precompilation). For instance, we can set a default theme and master page for the a site in web.config:

<pages
     
theme="Theme1" 
     
masterPageFile="~/MasterPage1.master">
</
pages>

When performing a full precompilation, the ASP.NET compiler bakes the theme and master page settings into the generated code. This means if you go back and change the web.config to use Theme2 and MasterPage2, the settings won’t make a difference in a non-updateable web site. The site happily goes along using the web.config settings in effect when precompilation took place (Theme1, MasterPage1).

On one hand, the behavior makes sense, because we did ask for a non-updateable web site, right? On the other hand, we can change other areas (like a connection string or an authorization entry), and the website behaves differently. It’s just a case of knowing which settings are used during the parsing / compilation, and which settings are used at runtime.

If we want to specify the default theme in web.config, and use precompilation, and want the ability to switch the default theme in production, then there are still options.

The first option is to precompile to an updateable web-site. For a precompiled, updateable web site, the .aspx source code remains in tact and deployed. The ASP.NET runtime will regenerate code for the .aspx when the web.config setting changes.

A second option is to hook the Pre_Init event. Even on a non-updateable web site, the PreInit event can set the Theme and MasterPageFile properties of a form to override the web.config settings. The configuration API makes it easy to read any updated web.config settings.

posted by scott with 3 Comments

Today’s Reading List

I had two grueling flights to get to Redmond today. I feel like I left home about 1 week ago, although the trip only took 9 hours.

Along the way, I plowed through:

The March issue of MSDN Magazine. Jeffrey Richter’s Optex article was a good read, and “Legal Doesn’t Think the Way You Do” was excellent. I think a regular column giving the perspectives of the non-developer roles we interact with would be enlightening.

One Night @ The Call Center. I don’t think this book is for sale in the U.S.A, and probably never will be. I got the book as a gift. The story centers on a group of friends who work at a technical call center in India. The Americans who call are all idiots and can’t operate basic kitchen appliances. The writing is funny and insightful, and I could connect with the characters. Worth a read, if you can find a copy.

The March issue of National Geographic. There was a fascinating story on using DNA to trace the journey of humanity of out Africa. Another story covered the environmental impacts of coal mining – which I’ve seen up close. It’s not pretty.

Eventually I do what I always do on an airplane. Close my eyes, listen to music, and pretend I’m not on an airplane. Never works.

posted by scott with 3 Comments

As Time Goes By

I looked at the following code and thought it should replace a file’s existing extension with “.xml”.

string newName = null;
newName =
Path.GetFileNameWithoutExtension(
                    fileName +
".xml"
                );

It might be easy to spot in isolation, but when “foo.log” didn’t transform into “foo.xml” on disk, I went looking for the hard stuff first – swallowed exceptions, bad conditional logic, etc. I didn’t see the misplaced paren until later.

string newName = null;
newName =
Path.GetFileNameWithoutExtension(fileName)
                    +
".xml";

Another waste of time was a production app that looked like it was running under a user’s credentials instead of the NETWORK SERVICE account. A quick glance at the badly formatted web.config file seemed to indicate everything was setup properly. Impersonation should only be on for requests in the ManagementStuff directory.

<configuration>   
<
system.web>
   <
compilation debug="true"/>
   <
authentication mode="Windows"/>
   <
authorization>
      <
deny users="?"/>
      <
allow users="*"/>
   </
authorization>
   <
customErrors mode="Off" />      
</
system.web>
   
<
location path="ManagementStuff" />
   <
system.web>
      <
identity impersonate="true"/>
   </
system.web>
</
configuration>

This time I went off looking at IIS settings, machine.config settings, and even had FileMon and Process Explorer running. After some time I returned to the web.config and noticed someone closed the location element tag too early. ASP.NET was using impersonation for the entire application.

<location path="ManagementStuff" >
   <
system.web>
      <
identity impersonate="true"/>
   </
system.web>
</
location>

While on the topic of wasting time – when using Google to search for “Tablet PC”, the first ‘sponsored link’ to appear is www.Dell.com. It’s nice of the Dell sales and marketing department to sponsor this link, considering they don’t sell a Tablet PC. I wonder how long it takes the average web surfer to figure out there are no Tablet’s available, and how many of them feel deceived.

Occam’s razor can save us time with the debugger, but can’t save us from salespeople.

posted by scott with 5 Comments

Inside AutoEventWireup

The following information applies to ASP.NET 2.0

Basics

The default value for AutoEventWireup is true for a C# web form, and false for a VB.NET web form. The IDE adds the default values to the @ Page directive for a new web form. The difference in defaults is partly because VB.NET has a mechanism for defining an event handler and subscribing to an event in one graceful motion (the Handles keyword).

Protected Sub Page_Load(ByVal sender As Object, _
                        
ByVal e As System.EventArgs) _
                        
Handles Me.Load
    
' ...

End Sub

An easy way to generate the above code is to use the drop down controls that sit just above the editor. Note: C# doesn’t make the dropdown list of events available when editing a code-beside file, but the dropdown is available when writing in-line code.

There is no equivalent to the Handles keyword in C# (anonymous event handlers are arguably close, but just not the same). When AutoEventWireup is true, all we need to do is follow the method naming convention of Page_EventToHandle. The ASP.NET runtime will automatically find and fire the method for the appropriate event.

protected void Page_Load(object sender, EventArgs e)
{

}

Once Again, In Reverse

If we switch to AutoEventWireup=”true” for a VB.NET web form, we can use the magic Page_EventName approach. The only change to the earlier VB.NET code would be to drop the Handles clause, and the events fire correctly.

If we switch to AutoEventWireup=”false” for a C# web form, there is a little extra work to do. Somewhere we need to explicitly wire up events. Here is one approach.

public partial class _Default : Page
{
  
public _Default() // ctor
   {
      Load +=
new EventHandler(Page_Load);
      PreInit +=
new EventHandler(Page_PreInit);        
   }

    
protected void Page_Load(object sender, EventArgs e)
    {
      
// ...
    }

  
protected void Page_PreInit(object sender, EventArgs e)
   {
      
// ...
   }
}

Here are the methods the runtime will look for when AutoEventWireup is true.

  • Page_PreInit
  • Page_Init
  • Page_InitComplete
  • Page_PreLoad
  • Page_Load
  • Page_LoadComplete
  • Page_DataBind
  • Page_SaveStateComplete
  • Page_PreRender
  • Page_PreRenderComplete
  • Page_Unload
  • Page_Error
  • Page_AbortTransaction
  • Page_CommitTransaction

Trivia

When AutoEventWireup is true, the runtime has to look for each of the page event handlers using code like the following.

bool ignoreCase = true;
bool throwOnFailure = false;
Delegate d = null;

d =
Delegate.CreateDelegate(
                      
typeof(EventHandler), this,
                      
"Page_Load", ignoreCase,
                      throwOnFailure
                    );

Notice the member search is case-insensitive, which isn’t surprising since VB.NET is case-insensitive. What is surprising (to me) is that the following works, too.

protected void Page_Load()
{
  
// ...
}

If the runtime can’t make an EventHandler delegate, it will then try to make a delegate to a method with an empty parameter list. Yes, that's up to 28 calls to CreateDelegate per web form with AutoEventWireup="true".

posted by scott with 25 Comments

dnrTV and The Amazing DXCore

The latest episode of dnrTV with Marc Miller and Karl Franklin is fascinating. Karl asked his blog readers to submit ideas for a tool that Marc would build using the DXCore framework. I suggested building a tool to rearrange the code inside a class based on accessibility modifiers and some other criteria, as I’ve often rearranged strange code by hand to fit a style I’m accustomed to. Marc finished the feature in less than an hour.

DXCore kidnaps the Visual Studio extensibility model and hides it in the trunk of a late model Buick. DXCore replaces the extensiblity API with a a richer object model for building plug-ins. DevExpress builds it’s own commercial refactoring plug-ins on top of DXCore. The API is well organized, but huge. Once you’ve learned your way around, however, you can bend the IDE to your will.

Marc and Karl also referred to me as Paul, but I don’t mind. I mix up names all the time.

posted by scott with 0 Comments

The Next MS Office Version

This might be old news to some, but reports say Microsoft is thinking of making a free, ad-supported version of Office.

I’d like to beta test the software to see what it can do. I’m wondering how smart the ads can be. There is smart, as in "keyword smart", and then there is really smart...

 

posted by scott with 3 Comments

Declarative Versus Imperative Coding

andyclap commented on my recent “Life With XAML” article:

“Did the brave OO language research guys give (the better part of) their lives in vain; their graves to be desecrated by the vile 1970s SGML nincompoops?

From this sample, the only function of XAML seems to be to put the foul-tasting XML syntactic sugar around a very ordinary presentation paradigm.”

Colorful language, andyclap, I enjoyed reading the entire comment. I wish my article had gotten across the point that object orientation isn’t dead with declarative markup. Many of the articles I’ve read about WPF (a.k.a. Avalon) overstress the loving embrace of angled brackets, and I tried to put in a slightly different spin. Non-trivial WPF applications still need both declarative XAML and imperative code.

In many scenarios it’s easier to describe what the goal is that you want to achieve, instead of descrtibing how to get to the goal.

I’ll turn again to an ASP.NET example. Here is some code imperatively building part of a webform.

Label label = new Label();
label.ID =
"Label1";

Panel panel = new Panel();
panel.ID =
"Panel1";

panel.Controls.Add(label);
form1.Controls.Add(panel);

We must specify the commands to reach the goal: we want the form to contain a Panel object, and the Panel object to contain a Label object. Unfortunately, all the commands obscure the goal, particularly as we add more controls and a deeper hierarchy. It’s not surprising that ASP.NET lets us use a declarative style, as HTML is a declarative language and the two need to intermingle. The declarative form would look like the following:

<form id="form1" runat="server">
    <asp:Panel runat="server" ID="Panel1">
        <asp:Label runat="server" ID="Label1" />
    </asp:Panel>
</
form>

In the declarative form, the goal is easier to see, and consequently easier to understand, maintain, and modify. Declarative programming is also easier for tools to understand, which leads to more designers to visualize declarative programming compared to imperative programming.

Still, everything we do in software requires a tradeoff, and declarative programming isn’t well suited for every task. In my article I decided it was easier to use imperative code and a loop to define a grid instead of using XAML. On the other hand, workflows come to mind as a tasks that are easier to specify declaratively. Since the runtime and our hardware are all imperative, imperative programming will still be around for a long time.

posted by scott with 2 Comments

The Partner Program Is Out Of Control

The Microsoft Partner program needs to simplify and streamline itself. Becoming a partner used to be a simple process with simple rules. Now there are  “bonus points” and other marketing tactics that make the program feel like a frequent flyer program. Partnerships are about marketing, but one day I won’t be surprised if someone calls to tell me I only need 2 more ‘partner points’ to get preferred ‘gold parking’ at the local Microsoft field office.

I just want software licenses.

I don’t have time to read the constant stream of emails, and I haven’t had the time the last two months to answer the numerous phone calls. Everyone who calls is pleasant and tries to be helpful, but no one can explain some of the deep voodoo in the system.

Take this one example…

I hold an MCSD certification. The SD stands for ‘solution developer’. To earn an MCSD I had to take certification exams covering programming tools and programming languages. The computers in the basement of the partner program science lab have analyzed my certification transcript and have decided I'm qualified for a single competency: the “Advanced Infrastructure Solutions” competency. This competency is for those who specialize in Active Directory and Exchange Server.

I installed Exchange once. I installed Biztalk once, too. Those were two of my proudest software installation moments ever.

I hope the program will offer less fluff, provide more licenses, and fix the wacky backend software.

posted by scott with 3 Comments

Cargo Cults

Perhaps you’ve heard the term ‘cargo cult programming’. Eric Lippert defined the term as follows.

There are lots of cargo cult programmers -- programmers who understand what the code does, but not how it does it. Therefore, they cannot make meaningful changes to the program. They tend to proceed by making random changes, testing, and changing again until they manage to come up with something that works.

The term derives from Richard Feynman’s “cargo cult science”. Feynman coined the term in a 1974 commencement address at Caltech. The speech transcript is a good read.

In the South Seas there is a cargo cult of people. During the war they saw airplanes with lots of good materials, and they want the same thing to happen now. So they've arranged to make things like runways, to put fires along the sides of the runways, to make a wooden hut for a man to sit in, with two wooden pieces on his head to headphones and bars of bamboo sticking out like antennas--he's the controller--and they wait for the airplanes to land.

The reason I bring this up is a Smithsonian Magazine article I read last week: “In John They Trust”. The author visited the remote island of Tanna to report on a real, enduring cargo cult. Villagers on Tanna worship John Frum, their American messiah. John lives in a volcano. John promised to return and bring shiploads of cargo, including bottled soda, canned meat, candy bars, and refrigerators.

Human behavior never ceases to amaze.

posted by scott with 3 Comments

Namespaces and ASP.NET 2.0

I've seen grumbling lately over the lack of namespaces in ASP.NET 2.0 projects.

With Visual Studio 2003, if I add a page with the name of WebFormA to a folder with the name of FolderA, inside a project (named ProjectA), then the IDE starts off with a code-behind file that looks like the following.

namespace ProjectA.FolderA
{
   
/// <summary>
   /// Summary description for WebFormA.
   /// </summary>
   public class WebFormA : System.Web.UI.Page

The same steps with web project model in Visual Studio 2005 produces the following.

public partial class FolderA_WebFormA : System.Web.UI.Page
{

Some people believe having a class declared outside of a namespace is an unthinkable tragedy. My question is why?

One reason to use a namespace is to avoid type name collisions. By appending folder names to the front of the class, ASP.NET is avoiding name collisions (although I do wonder when someone will bump up against the max identifier length).

A second reason for using a namespace is to present a logical, organized hierarchy for consumers of the types. Who is the consumer of a WebForm? Typically only the ASP.NET runtime, which doesn’t need a namespace to help it map an HTTP request to an HttpHandler.

Types in a code-beside file are well hidden. The compilation model for ASP.NET 2.0 may even put each type in a separate assembly. In the scenarios where a Page, UserControl, or MasterPage code-beside type is needed outside of it’s own definition, the @ PreviousPage, @ MasterType, and @ Reference directives are needed. Pretending that these types are anonymous isn’t a bad idea. Using a base class or an interface will reduce the tight coupling that occurs when user control and master page types are used by other areas of an application.

If you still need to put Page derived types in a namespace, you'll want to consider using the Web Application Project type, which works like the 1.1 model.

posted by scott with 13 Comments

Care and Feeding Of Community Server

Over the weekend, OdeToCode bumped up against it’s SQL Server disk space quota. Some operations, like adding comments, were throwing exceptions because the primary filegroup was full. Instead of paying for more disk space, I looked to see if there was some extra baggage I could get rid of.

Step 1 of the adventure was to verify the database size. The sp_helpdb stored procedure verified that the database was using almost all of the 100MB allocated by the ISP.

Step 2 was to see investigate the space reserved by each table in the database. The sp_spaceused stored procedure can list the amount of disk space used and reserved by a table, but only for one table at a time. The trick is to execute sp_spaceused with sp_MSforeachtable. sp_MSforeachtable is an undocumented tidbit inside SQL Server, but you can find information around the web (see Raymond Lewallen’s post, as a good example). The following script will display the space reserved and used for each table in the current database.

CREATE TABLE #TableSizes
(
  table_name
SYSNAME,
  row_count
int,
  reserved_size
varchar(10),
  data_size
varchar(10),
  index_size
varchar(10),
  unused_size
varchar(10)
)

INSERT #TableSizes
   
EXEC sp_MSforeachtable 'sp_spaceused ''?'''

SELECT * FROM #TableSizes ORDER BY table_name

The good news from the script was that content (blog posts, articles, and comments) only needed 10MB of storage. I still have plenty of headroom for incoherent ramblings.

Three tables stood out for using almost 70MB of the site’s 100MB quota: cs_Urls, cs_Referrals, and cs_SearchBarrel. I didn’t want to take away any of the search capabilities, as I actually use the search feature myself. The other two tables, however, track who has been coming to the site and from what location. I tend not to make use of this information, so the next step was to free up 60MB of space:

BEGIN TRANSACTION

DELETE FROM
cs_Referrals WHERE LastDate < '1/1/2006'

DELETE FROM cs_Urls
WHERE UrlID NOT IN
  (SELECT UrlID FROM cs_Referrals)

I chased the above with a COMMIT TRANSACTION once I was sure I had typed in the right table names.

At this point, the database was still as big as it ever was. I’ll admit I’m a bit hazy as to how and when SQL Server gives back reserved free space. I can only tell you what worked for me:

DBCC DBREINDEX (cs_Urls)
DBCC DBREINDEX (cs_Referrals)
DBCC SHRINKDATABASE(<database_name>)

DBCC DBREINDEX rebuilds all indexes. Since both cs_Urls and cs_Referrals have a clustered index, DBREINDEX effectively rebuilt the tables themselves, and the amount of reserved space for both dropped. DBCC SHRINKDATABASE then shrank the data file to under 55MB, and peace returned to the site. The irony is, I needed free space for these operations to free up space. As dbo I could bend the rules temporarily to make this happen.

posted by scott with 13 Comments

Life With XAML

It’s the Game of Life, and it’s written for WPF.

Read the article. Download the code.

posted by scott with 3 Comments