All of your bases are belong to us! ASP.NET-Style

I usually find .NET error messages very informational, and then I met this one:

Parser Error Message: The file 'src' is not a valid here because it doesn't expose a type.

Say what? 

I was seeing this error after compiling and deploying my application.  It would run fine for an hour or so, and then tilt -- and badly.  The usual means of app domain recycling (IISRESET, change web.config) didn't clear the error; the only way I found to reset the app domain was to the DLL file in \bin.  Yikes.

I'll spare you the details of the this error, except to note that it only appeared for pages with user controls.   The parser error masks the real issue which you can see when you browse a page without user controls:

Unable to cast object of type 'System.Web.Compilation.BuildResultCompiledAssembly' to type 'System.Web.Util.ITypedWebObjectFactory'.

Cool, a sensible InvalidCastException error (albeit with a scary stack trace deep in the bowels of ASP.NET compilation).  Fortunately this one has a KB article with an explanation:

This problem is caused when a satellite assembly that has a nonneutral culture is located directly in the Bin folder of the Web application. Satellite assemblies must be located in a subfolder of the Bin folder. Additionally, satellite assemblies must have a name that is equal to the culture of the assembly, such as en-GB.

 I don't have any satellite assemblies in my application, but I did have an assembly-level attribute setting the culture to "en-US".  Clearing the attribute cleared the problem.

[assembly: AssemblyCulture("")]

This problem was definitely self-inflicted.  I was planning for localization and meant to add the System.Resources.NeutralResourcesLanguageAttribute.  I used the AssemblyCulture attribute already in the AssemblyInfo.cs file by mistake.  The only unanswered question I have: if setting AssemblyCulture on the application's main DLL breaks the app, why is it in the default AssemblyInfo file?

Reading a config section from an external file in ASP.NET 2.0

In my current project, we have a requirement to store some of the application settings in separate files from web.config.  Some application settings will change often enough that we'd like to export them from a forever-behind-the-firewall database to a file we can drop into the public website.  On the other hand the basic web.config settings will rarely change, and it could really mess up the application if the autogen'd settings somehow stomped an important configuration value in web.config.  Keeping these two kinds of settings in separate files helps make this approach viable.

As it turns out, this particular application uses three different mechanisms for referencing settings in an external file, so I've had a chance to compare and contrast the approaches.

1.  <appSetting>'s file= attribute.

2.  Any config section's configSource= attribute.

3.  A custom config section of type System.Configuration.AppSettingsSection.  This is essentially the same mechanism as #1, but you can create as many separate configuration files as you need using custom sections.


 

<appSetting>'s file=

The <appSetting> file mechanism is both simple and flexible, and probably "enough" for most ASP.NET 2.0 applications that need an external settings file.  The AppSettingsSection class allows supports loading settings from both an external file as well as declaring them inline in web.config:

In web.config:

<appSettings file="moresettings.config">
    <add key="first" value="inline settings are loaded before external ones" />
</appSettings>

In moresettings.config:

<appSettings>
    <add key="second" value="another setting" />
    <add key="third" value="yet another setting" />
</appSettings>


Notes:

  • the ACLs of the moresettings.config file must allow the ASP.NET application to read it
  • the root node of the moresettings.config file must be <appSettings>
  • key/value pairs declared inline in web.config appear first in the resulting NameValueCollection


Using the settings in code is easy:


using System.Configuration;
...
string first = ConfigurationManager.AppSettings["first"] as string;

When you access appSettings in code, all of the settings are members of the same collection despite being declared in separate files.  Your application could use this mechanism alone to segregate specific application-scoped settings to another file.

 

<urlMapping>'s configSource=

The configSource attribute can be used to specify an external source file for any ASP.NET 2.0 config section.  Unlike the file attribute, there's no merging of config settings between web.config and the external configuration file; you must move the entire section to a separate file.  The section tag (in this case <urlMappings>) must be the root element of the external file.

In web.config:

<urlMappings configSource="friendlyurls.config" />

In friendlyurls.config:

<urlMappings>
    <clear />
    <add url="~/Home.aspx" mappedUrl="~/Default.aspx?tab=home" />
</urlMappings>

 

Custom config section using System.Configuration.AppSettingsSection

There is plenty of documentation on MSDN for creating custom config section handlers in code, and in cases where you want to parse custom XML tags the full metal approach makes sense.  But if you just need another group of key/value pairs it's much simpler to use the existing System.Configuration.AppSettingsSection class.  This is the same class used for <appSettings> so it has the added benefit of permitting you to merge inline and external settings into a single KeyValueConfigurationCollection at runtime.

In web.config:

<configSections>
    <section name="regions" type="System.Configuration.AppSettingsSection, System.Configuration, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" requirePermission="false" />
</configSections>

<regions file="regions.config">
    <add key="BE" value="Europe" />
</regions >

In regions.config:

<appSettings>
    <add key="US" value="North America" />
    <add key="CA" value="North America" />
</appSettings>

 

Reading the settings from a custom AppSettingsSection is more work than from the default AppSettings section.  And web.config heirarchies and the WebConfigurationManager.OpenWebConfiguration() method are just flexible enough to make grabbing the right instance of your web.config a little tricky.  I found the documentation and sample in the ASP.NET Quickstarts helpful in decyphering the options.  Here's some code that reads settings from a custom AppSettingsSection in the current application's root web.config:

using System.Configuration;
using System.Diagnostics;
using System.Web;
using System.Web.Configuration;

...

// Determine web path of the application's web.config
string path = HttpContext.Current.Request.CurrentExecutionFilePath;
path = path.Substring(0, path.LastIndexOf('/'));
if (path.Length == 0)
    path = "/";

// Open the config section as an AppSettingsSection
Configuration rootWebConfig = WebConfigurationManager.OpenWebConfiguration(path);
AppSettingsSection regionSection = rootWebConfig.GetSection("regions") as AppSettingsSection;

if (regionSection != null)
{
    // Get the appSettings key/value pairs collection
    KeyValueConfigurationCollection regionSettings = regionSection.Settings;

    foreach (KeyValueConfigurationElement item in regionSettings)
    {
        Debug.WriteLine("key: " + item.Key.ToString());
        Debug.WriteLine("value: " + item.Value.ToString());
    }
}

Adventures in CSS: Authoring for Multiple Browsers

The CSS for Windows Marketplace 2.0 was written to target the three big marketshare browsers: IE6, IE7, and FireFox 1.5.  We've found it works pretty well in other browsers (like Opera) even though we didn't specifically target them.  That's probably due to compliance to standards for XHTML and CSS, go standards!

Rather than applying browser-specific styles via hacks or script, we used IE's conditional comment feature.  Alan blogged a little bit about the technique during the project, but I wanted to drill deeper into the Windows Marketplace implementation since it's such a powerful approach to cross browser support.  I like it a lot over the alternatives, since it's hack-free and javascript-free -- hats off again to Robert Biggs for setting us on this path.  Conditional comments also promote encapsulation which is a wonderful way to help the maintainability of the application.  Hacks, on the other hand, are horrible for maintainability: it's nearly impossible to find folks with the requisite encyclopedic knowledge of the arcane CSS hack-scape, let alone those with enough javascript and front end UI dev experience to debug whether a problem is code vs. script vs. CSS.

 

Approach

Of the three browsers we wanted to support, only IE6 and IE7 knew how to use conditional comments.  This fact, coupled with the fact that IE is less standards-compliant than FireFox, dictated our approach:

  • create the baseline CSS for FireFox first
  • use IE conditional comments to include "override" versions of style sheets for IE6 and IE7 as needed

We modularized this a bit further and broke the style sheets up by functionality.  So the style sheets for the frame -- frame.css, frame-ie6.css, and frame-ie7.css -- are included on all pages whereas the styles for wish list feature are included only on Wishlist.aspx.  The site has 40 or so style sheets altogether.  That may seem like a lot of different style sheets to manage but knowing which one to touch when you need to make a change is surprisingly intuitive.

 

Example

Here's a sample of the style sheet links from the search results page.  The first group (shown in blue) are the FireFox styles, many of which also work in IE.  The next sections are commented; FireFox ignores them completely.  The "lte IE 6" section is included by versions of IE "less than or equal to" version 6.  In practice this includes IE 5 (the first version to recognize conditional comments) plus IE 6.

The "gte IE 7" section is included by version of IE "greater than or equal to" version 7.  There's a tiny leap of faith here that versions of IE will become ever more standards compliant in the future; sure hope we're right!

In practice, we found that IE6 needed a lot of tweaks from the standard CSS from the site, whereas IE7 needed very few.

<link rel="stylesheet" href="/mapasset.aspx?file=css/frame.css" type="text/css" />
<link rel="stylesheet" href="/mapasset.aspx?file=css/layoutResults.css" type="text/css" />
<link rel="stylesheet" href="/mapasset.aspx?file=css/searchResults.css" type="text/css" />
<link rel="stylesheet" href="/mapasset.aspx?file=css/globalLayouts.css" type="text/css" />
<link rel="stylesheet" href="/mapasset.aspx?file=css/tooltip.css" type="text/css" />
<link rel="stylesheet" href="/mapasset.aspx?file=css/modules.css" type="text/css" />
<script type="text/javascript" src="/scripts/winmp.js"></script>

<!--[if lte IE 6]>
    <link rel="stylesheet" href="/mapasset.aspx?file=css/frame-ie6.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/tab-ie6.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/globalLayouts-ie6.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/searchResults-ie6.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/tooltip-ie6.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/modules-ie6.css" type="text/css" />
    <script src="/scripts/ie6submenu.js" type="text/javascript"></script>
<![endif]-->

<!--[if gte IE 7]>
    <link rel="stylesheet" href="/mapasset.aspx?file=css/frame-ie7.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/tab-ie7.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/globalLayouts-ie7.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/searchResults-ie7.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/tooltip-ie7.css" type="text/css" />
    <link rel="stylesheet" href="/mapasset.aspx?file=css/modules-ie7.css" type="text/css" />
<![endif]-->

 

You may have noticed the mapasset.aspx target of the style sheet link above; that was required to set the paths of CSS, script and image assets via configuration per deployment.  I'll blog about that in detail sometime.  What the browser sees is just a standard CSS file, and it caches it accordingly.

Adventures in CSS: Stretchy Frame

One of the surprising CSS challenges for Windows Marketplace was stretching the spiffy "glass" frame to fit the content.  The visual design of the site is intended to suggest Windows Vista: a glassy frame in the center of the page around the content, yummy background gradients filling to the width of the browser, even the grassy texture behind the footer.   Despite the visual sophistication of such a design, it should be easy to layout, right?  I mean, with tables I could just slice those images up and make the pieces the background for various cells...

It sure seemed easier to do a table layout but doing so would have put a stake through the heart of the semantic web hopes for the website.  So we tapped CSS guru Robert Biggs for some help with this and other CSS challenges for the site.  Here's the technical breakdown of the stretchy frame problem, and how Robert solved it.

Requirements:

  • Vista-like glassy effects in IE6, IE7, FireFox 1.5+
  • Scales vertically to the content.  (Scaling horizontally was not a requirement)
  • Looks good on monitors with high resolution or wide aspect ratios.

Design:

The design called for two gradient background fills: one at the top and another at the bottom.  A fixed-width frame is centered on top of the gradients, which stretch for the width of the browser.  On high resolution monitors, instead of have the web content parked in the left side of the browser with a whole bunch of white space on the right, the browser frame is filled.  It looks really nice. 

As the frame stretches vertically, the white background between the two gradients increases.  The sides of the glass frame stretch over the white region as well, of course.

     

One constraint we needed to decide up front was the minimum height of the content region (the white region in the picture above): 700px.  This ensured the gradients would never collide.    Robert implemented the graphics to make both gradients blend to the center color.  In markup, he nested two <div> tags just inside the body.  He attached the top gradient image to the outer div as a background image, and the bottom gradient in the inner div as a background image.  This permitted the page to scale vertically as long as needed. 

Here's the CSS and markup -- it's pretty simple:

#bodyBackground { background-image: url(%img%/frame_elements/bkgd-gradient-top.gif); background-repeat: repeat-x; background-position: top left; }

#main { background-image: url(%img%/frame_elements/bkgd-gradient-bottom.gif); background-repeat: repeat-x; background-position: bottom left; }

#footerBackground { background-image: url(%img%/frame_elements/footer-bkgd.jpg); background-repeat: no-repeat; background-position: top left; }

</head>

<body>

<div id="bodyBackground"> 

    <div id="main">

        <div id="header">
          <!-- branding, menus, search -->
        </div>

        <div id="bframe">
          <!-- content region -->
        </div>

        <div id="footer">
          <!-- bottom search -->

          <div id="footerBackground">
              <!-- links -->
          </div>
        </div>

    </div>

</div>

</body>

 

The last challenge for the stretchy frame was the grassy texture behing the footer.   This texture is a background picture for the "footerBackground" div nested in the "footer" div (outlined in blue in the picture below).   Since the page sizes itself to the content, including footer content, this div is always at the bottom of the page.  The trick of pinning the bottom gradient to the bottom of the "main" div ensures that the gradient and image always line up perfectly.

 

 

Adventures in CSS: Windows Marketplace 2006

Often when you make the "sequel" to an existing application, several of the requirements boil down to fixing what didn't work well in V1.  For the original Windows Marketplace, one of the largest pain points was the lack of editorial control over just about everything: layout, style, content -- pretty much anything needed to keep the site fresh and compelling. 

So it's no surprise that one of the key requirements for the latest version of Windows Marketplace was lots of editorial control over just those things: layout, style, content, navigation, A/B testing, tracking, and all the other knobs the editorial staff needs to turn.  We made the call (some might say the leap of faith) to rely on CSS positioning for the core elements of the site, as well as CSS styles for much of the behavior of the site.  The content management system would emit only semantic markup; CSS would do the rest.

We had to learn quite a few CSS tricks to pull this off, but in the end using CSS has satisfied the editorial flexibility requirement with flying colors.

 

Home page with CSS:

Same page without CSS:

Source snippet (semantic menus):

A picture says 1000 words (and saves a dozen meetings)

A "straw man" is simply a hastily-assembled approximation of a deliverable, and an incredibly effective tool for driving fast group decision making.  It will be wrong in many of the details, but it gives the folks in the room something to correct or elaborate upon and sidesteps the "blank sheet syndrome" where many design meetings fail to get down to specifics.

Tips for producing an effective straw man:

  1. No straw men on the first date!  Spend some time listening to the client (or the team) before offering up a straw man.  There needs to be some open-ended discussion around the problems you are trying to address before you start to pencil out solutions. 
  2. Put the straw man in the most informal format that will work for your discussion.  If you are trying to get to a list of requirements, for example, a list in email is perfectly acceptable
  3. Assign the task of authoring the straw man to just one person.  As soon as you make it a team effort, you lose the efficiency gains of using a straw man.
  4. When it's your turn to write the straw man, think about the decisions you'd like the group to make by the end of the meeting and make those the target of your strawman.   
  5. Don't spend so much time on your strawman that folks are discouraged from are encouraged to pitch in and make it better.

Tips for presenting a straw man:

  1. Before you present your strawman, clearly state that it's just an example meant to stimulate conversation.
  2. If the strawman fails to stimulate the right discussion, set it aside.  It's just a tool, and sometimes you discover you really want the pliers instead of the screwdriver. 
  3. Listen carefully to feedback and write it all down.  After the meeting you may choose to polish the strawman into a more permanent document, or capture the decisions elsewhere.

Localizing Web apps in ASP.NET 1.1

I've had a chance to localize a couple of pretty beefy web applications in the past year running ASP.NET 1.1.  While I think 2.0 will give us even better tools for localization, the reality is that we're going to be working with ASP.NET 1.1 applications for some time to come.  This article describes a strategy for localizing ASP.NET 1.1 applications that has proven to be maintainable, flexible and a good performer under heavy load. 

Soft Skill #5: Public Speaking

Public speaking is an “advanced soft skill: the ability to speak well in front of audiences large and small can take years of training and practice to truly master.  But the 80/20 rule works here too, so I've listed my tips for the first 20% that can make anyone an effective public speaker.

#1 Public speaking is a people thing.
Only heads of State and PR pukes deliver meticulously-practiced, pre-written speeches word-for-word.  And there's a reason: it's tedious to listen to such speeches.  So unless you are delivering new information where the slightest mistake could cost you your job or your life, don't orate.

People enjoy watching other people -- it's human nature.  One of the most effective ways to engage your audience is to be yourself!  Act in a way that is natural for you, and give folks a glimpse into your personality.  Your audience will feel like they are getting to know you, in addition to learning about your topic.

Being yourself in front of an audience, of course, requires an excellent command of the proto-soft skill: acting with confidence.

#2 Make good use of your Communications 101 skills.  Know your material, know your audience, give your audience a reason to care about your topic, and organize your thoughts.

#3 Don't read your slides.  Reading your slides word-for-word can be insulting to the audience since it implies they can't read.  Oops.  It also broadcasts that you don't have any additional knowledge or perspective to add on your topic.  In other words, you are wasting their time.  Reading the slides is a guaranteed way to kill your speech dead.

There are two techniques you should apply to avoid the pitfall.  First, know your topic (d'oh).  If you don't know the topic, cram before the speech or get someone else to give it.

Second, distill your slides down to just a few words: a good goal is no more than 5 bullet points per slide, and no more than 5 words per bullet point.  If you can, try not to let a bullet point exceed one line!  This lets the audience get a gist of what you are going to talk about, but makes them listen to you for the information.  Which is cool, since that's the point of attending a speech. ;-)

#4 Speak loudly.  This is a sort of “stupid pet trick for public speaking, but it really works.  Don't shout, but try to speak loudly.  Not only with that ensure folks can hear you, but it's a great trick to convey energy and maybe enthusiasm -- which in turn will help your audience engage.

#5 Speak slowly when you need to compensate for being hard to understand.  Do you naturally speak very fast?  Do you have a strong accent in the language you using for the speech?  Folks who don't know you might need a little help adjusting to your speaking style.  Speaking loudly will definitely help, but you can also compensate for an accent or a rapid-fire delivery by simply speaking a little more slowly.

#6 Rehearse your speech in advance to time it.  It certainly won't hurt to practice the material, but the point of this technique is to tune your material to fit into the time allotted for your speech.  Running long is frustrating to audiences, as is running short (although this is a fairly rare problem).

Powerpoint has an awesome feature to automatically time your run-through: Slide Show > Rehease Timings.  If you find you are running too long, condense your material!  The delivery time of a speech almost always grows 10%-20% in front of a live audience.

Soft Skill #4: Traction

“Traction” is another one of those Microsoft-speak words (like “rathole” or “dogfood”). It means a person's ability to drive for a specific result.  For example, “JoeN has amazing traction right now on getting generics into C#.”

You might also think of traction as “achieving results” or “meeting goals,” but the term - and the perspective - is focused on the process rather than results.  And that's a good thing.  As individuals there are many factors we can't control that may prevent us from meeting a goal.  For example, when your project is cancelled you aren't going to meet your goal of shipping on a particular date.  But by developing the soft skill of driving to a specific result, you'll consistently meet your goals when circumstances allow.

There are some simple steps you can take every day to improve your traction.

#1 - Know where you are headed.  As George Harrison sang, “If you don't know where you are going, any road will take you there.”  The catch is, of course, when you arrive 'where ever' you will probably wish you ended up somewhere else. ;-) Know where you are headed, and you will likely to take a fairly direct path to your destination.

#2 - Aim for milestones along the way.  If you've ever taken a road trip, I'm sure you can relate to this.  Your ultimate destination might be Miami, but you are very likely to stop for food, rest and gas along the way.  At minimum you need to plan your next gas station before your tank runs dry.  But not too often!  If you stopped at every gas station along the way, it would take a looooooong time to reach Miami. 

Milestones make reaching a goal more manageable.  Choose a milestone, you put your head down and push forward your progress for a bit.  When you reach it you can stop, take a look around and course correct if necessary. 

Set milestones that let you process enough work to make reasonable progress, but don't scope them so broadly (to an entire project, for example) that your course corrections when you finally reach the milestone are massive. 

#3 - Clear your unknowns as early as possible.  In our business, it's generally the 'unknowns' that force the really big course corrections.  Identify what you don't yet know about your project, and make a milestone to figure it out up front.  For example, maybe you are considering using ASP.NET 2.0 for a project.  Set knowing the answer to the ASP.NET 2.0 question as an early milestone toward your project's destination.

#4 - Communicate your progress toward your current milestone and final destination.  Let your co-workers and client know where you are headed, and how you are progressing.  Milestones and destinations are much easier to communicate than your entire “to do” list, and folks often find ways to help you arrive when they know where you are headed.  They may even think of tactics and resources that you've missed.

The 'when' and 'how' of communicating your progress is up to your individual style.  I personally like the 1-minute “elevator” speech: keep it simple, and deliver it personally to folks you see in the halls or in the lunchroom.  By keeping it “bited-sized” folks can probably stash away a couple of important details.  And by delivering it personally, you've also delivered a personal invitation for feedback and support later on -- and, in effect, added another part-time member to your team.

Soft Skill #3: Decision making

I haven't shared my definition of a soft skill yet, so I'll do that before I talk about decision making (which is the poster child of soft skills IMO).

A soft skill is a skill that lets you be effective in your job, but isn't directly related to your profession.  So teamwork is a 'soft' skill, whereas programming in .NET is a 'hard' skill.

So let's talk about decision making -- the ability to make timely, high-quality decisions.  It's one of the most essential soft skills once you advance past an entry-level job.  In a consulting company like Vertigo, it's indispensible: our clients come to Vertigo because they need the right thing built, not just anything.  They are paying for our exceptional professional judgement, the Vertigo 'secret sauce' of technical knowledge, experience, and (of course) decision making.

Decision making is a two step process.  First, you need to gather and understand the facts and the context for the decision.  Next, you apply analytic ability and common sense to the data to reach a decision.  Sounds simple enough, but there are some common pitfalls.

Analysis Paralysis.  This catchy term means getting stuck in the decision making process without ever reaching a decision.  Good decision making has to be timely in order to be effective, so analysis paralysis can be fatal.  Analysis Paralysis seems to mostly afflict folks without much decision-making experience. 

The cure for analysis paralysis is practice, practice, practice.  I specifically prescribe making 80/20 decisions.  Try for an 80% perfect decision, with just 20% of the effort require to get to the 100% perfect decision.  This is where that proto-skill confidence comes in handy.  You will make a few bad calls with this approach -- be sure to fess up to 'em and most importantly, learn from them.  But you'll also gain a lot more decision-making experience this way, and the quality of your decisions will improve dramatically.

Unteachable Factors.  Every skill, hard or soft, can only be acquired if you have some abilities as an individual.  For decision making these include native intelligence, common sense, and motivation.  It's really not practical to teach common sense, for example.  At best, you can approximate common sense by developing and using a 'play book' of good decisions for well known situations. 

We do a great job of testing for these factors in our interview process via the chess log exercise.  Be sure to watch for them as you interview potential co-workers as well.

Practice, Pay Attention to Your Results, Build Experience.  That's how you become better at decision making.