# Monday, January 14, 2008

The footer now appears at the bottom of the blog in IE (IE 7 anyway).

The content area is floating so that the blog now takes full advantage of your wide screen monitor.  It certainly makes this blog a lot easier to read in 1600x1050.

Monday, January 14, 2008 7:45:02 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 

I posted last week about testing JavaScript through NUnit with JSUnit.  While this worked, it was a little slower to run tests than I really wanted.  It was also slower (for me) to write the tests since I had to leave the comfort of NUnit and .NET for JSUnit and JavaScript.

I'm continuing to look at the problem of JS testing for ASP.NET.  I evaluated YUI Test, Yahoo's JS unit test library.   YUI Test actually looks very good, but I really wanted to keep things integrated with my current build and test process, even if it isn't the best solution; you don't know unless you try.  ;-)

This time I'm only using NUnit and WatiN for my JavaScript tests.  Since WatiN now has the ability to evaluate JavaScript and return the results, I'm using this to run the JavaScript function under test and then using an NUnit assertion for validation.

I create each test using regular NUnit syntax, and then wrap the JavaScript function call with a C# method which passes the arguments off to the JS function to test.

[Test]
public void ShouldValidateDiscover()
{
    Assert.IsTrue(ValidateCreditCard("6011648040903965", "discover"));
}
 
private bool ValidateCreditCard(string cc, string ccType)
{
    string testJs = string.Format("var cc = new CreditCardValidator(); cc.isValidCreditCardNumber('{0}', '{1}');", cc, ccType);
    return EvalToBool(testJs);
}

The EvalToBool method just calls the WatiN eval method which accepts JavaScript as a parameter, and then converts the JS string result to a .NET boolean.

protected bool EvalToBool(string js)
{
    string result = ie.Eval(js);
    return bool.Parse(result);
}

The overall process is:

  1. Create a dummy HTML page which includes the JavaScript source file I want to test.
  2. Kick off WatiN and navigate to the dynamically created HTML file.
  3. Pass the JavaScript function to be evaluated by WatiN.
  4. Assert the eval call results using NUnit.

The test setup is very simple, and it runs fast for this type of test.  On my PC it was taking 1.7 seconds to setup the test fixture, and then .2 seconds to run each test.  The nice thing is that the errors are generally obvious and easy to diagnose since they are native NUnit tests.  Its also nice to have multiple NUnit test entries in the runner instead of one monolithic JavaScript test function which encapsulated all my JS tests that run in JSUnit. 

The tests can get ugly if a JavaScript exception is thrown, in that case you get a generic COM exception returned from WatiN rather than a nice error message.  The is one area where a JSUnit or YUI test are naturally better at.

This makes me wonder if WatiN couldn't be extended to return better error information from JavaScript, and whether the use of a dynamic .NET language along with NUnit couldn't make for a nice testing experience.

Monday, January 14, 2008 6:44:21 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Sunday, January 13, 2008

I created a new utility called ConfigPoke for creating application configuration files from a template and one or more property files.  The property files allow you to create a set of property files in which each subsequent file overrides any settings in the previous.

The input property files are just plain text files with key value pairs, for example:

dbConnectionString        = server=localhost;database=Northwind;integrated security=SSPI
sessionConnectionString = server=localhost;database=AspState;integrated security=SSPI
smtpServer                  = localhost

 

This allow me to define a base set of shared developer properties and then I can define my own property file for me which overrides just properties, like the local database connection string.  I also define property files for each of my environments I deploy the application too: dev, test, release, live.

This utility integrates tightly with MSBuild and Visual Studio 2005/2008 using it's own target file.  There is also a command line interface for the application that can be used from NAnt or in your deployment process (NSIS in my case).

Per developer configuration happens automatically and is always up to date with the input template and property files with Visual Studio integration.  The output configuration files are conditionally built only if the input templates file or input property(s) files are changed, just like any regular source file in Visual Studio. If a Rebuild action is initiated in Visual Studio, the output configuration files are always rebuilt, even if the input files have not changed.

 

I've been using this utility for the past week at work, and it has worked really well.  Edit a property and everything just works.  Machine specific settings have proven invaluable especially on my laptop which has a lot of non-standard settings because I usually work disconnected on it.

 

You can download the MSBuild target DLL, console EXE, and MSBuild targets file from Google Code.  The code and the binary is release under the Apache 2.0 license.

Visual Studio 2005/2008 Integration Outline

  1. Import Sneal.Build.ConfigPoke.targets file into your VS project file.
  2. Create a ConfigTemplateFiles ItemGroup.
  3. Create a ConfigPropertyFiles ItemGroup.

Detailed Visual Studio 2005/2008 Integration Instructions

Include the Sneal.Build.ConfigPoke.targets file in your Visual Studio project file (csproj) using the following element:

<Import Project="Sneal.Build.ConfigPoke.targets" />

With the ConfigPoke MSBuild targets file included, the configuration building will automatically be hooked into the Visual Studio clean, build, rebuild process.

The Sneal.Build.ConfigPoke.targets file requires two MSBuild item groups as input: ConfigTemplateFiles?, and ConfigPropertyFiles?. These must be specified in your MSBuild (csproj) project file. Each ConfigTemplateFiles? item should be the destination filename + some arbitrary extension, I use '.template'. You may also probably want to set the "InProject?" item meta data to false so these templates or property files don't show up in VS solution explorer.

Here's an example ConfigTemplateFiles? ItemGroup? that you would put into your csproj file:

<ItemGroup>
  <ConfigTemplateFiles Include="$(MSBuildProjectDirectory)\Configs\web.config.template"/>
  <ConfigTemplateFiles Include="$(MSBuildProjectDirectory)\Configs\windsor.config.template" />
</ItemGroup>

To create user and machine specific overrides, you can include a base properties file and then optionally include a per user and per machine config, if they exist. This would allow each developer to create their own override properties file which can then be optionally checked into source control. To automatically set this up, you can take advantage of the built in MSBuild properties: USERNAME and COMPUTERNAME.

Here's an example ConfigPropertyFiles? ItemGroup? that you would put into your csproj file:

<ItemGroup>
  <ConfigPropertyFiles Include="$(MSBuildProjectDirectory)\Properties\App.Properties.base"/>
  <ConfigPropertyFiles Include="$(MSBuildProjectDirectory)\Properties\App.Properties.$(USERNAME)" Condition="Exists('$(MSBuildProjectDirectory)\Properties\App.Properties.$(USERNAME)')"/>
  <ConfigPropertyFiles Include="$(MSBuildProjectDirectory)\Properties\App.Properties.$(COMPUTERNAME)" Condition="Exists('$(MSBuildProjectDirectory)\Properties\App.Properties.$(COMPUTERNAME)')"/> 
</ItemGroup>

Note: You cannot pass in non-existant property files to the ConfigPoke utility, hence the Condition check above.

 

Optionally you can specify a ConfigPokeDirectory? and a ConfigOutputDirectory? property to override the default directories. The ConfigPokeDirectory? property should point to the directory where Sneal.Build.ConfigPoke.MSBuild.dll is located on your machine, if not specified this property will default to the current MSBuild project file directory. The ConfigOutputDirectory? property should point to the directory where the output configuration files are written too. If not specified this property will default to the current MSBuild project file directory.

 

For additional usage see the example in SVN, http://sneal.googlecode.com/svn/trunk/ConfigPoke/Sneal.Build.ConfigPoke.Example/Sneal.Build.ConfigPoke.Example.csproj

Sunday, January 13, 2008 1:39:35 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Saturday, January 12, 2008

I like to re-use code between projects, whether for personal use or professionally, so I created an Apache 2.0 licensed code repository.  Right now I just have my application configuration build tools committed (which I'll post about later).

The project home page is: http://code.google.com/p/sneal/

The project SVN URL is: http://sneal.googlecode.com/svn/trunk/

Saturday, January 12, 2008 8:01:44 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, January 10, 2008

The company I work for, GalleryPlayer in downtown Seattle (Pioneer square), is looking for a couple of software engineers to develop our next generation content distribution system.  I've worked for GP since last May and I really enjoy the work and the people

Why would anyone want to work for our small startup?

  • As an engineer you'll have a lot of influence over technology and design.
  • We prescribe to the Programmer Bill or Rights.  No you won't have your own office.
  • Our dev environment consists of: SVN, CC.NET, VS 2005, Re#, SQL Server 2000, NUnit.
  • Good location, Pioneer Square in downtown Seattle.  5 minute walk from the Sounder commuter rail station and bus lines.
  • Casual work environment - wear whatever makes you comfortable.
  • All the free, fresh, Starbuck's you can drink... and a toaster.  Yes, we trust you with a toaster!
  • We're using Castle Windsor, MonoRail, and Prototype for our web development backed by unit tests (and perhaps ASP.NET MVC when MS releases a Go Live license).
Thursday, January 10, 2008 9:03:38 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 

I consolidated a few duplicate categories and removed some others that just had one post under them to make the site a little more usable.  Next I'm going to work on the look and feel of this blog and make it work better in IE.

Thursday, January 10, 2008 8:30:23 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 

Developed data access class containing over 5,000 lines of code and over 100 methods

WTF
Thursday, January 10, 2008 8:28:45 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Wednesday, January 09, 2008

We've all written a few console apps in our time, but how often do we spend anytime creating a help command line switch that spits out how to use the app?  Hopefully you answered, "everytime."  There's nothing more annoying than a console app that doesn't display a man page if you forget to supply required parameters.

It's so easy to add syntax help, especially if you drop the Console.WriteLines in favor of an embedded text file resource.  This way you can edit your command line usage as plain text which is so much easier to write and maintain.  This may seem obvious, but often time we forget because, "it's just a quickie console app."  So the next time your find yourself writing a console utility, remember to use an embedded text resource as your help (or man) page.

EmbeddedResource

Other than actually creating the text file and marking it as an embedded resource, this all the code required (don't forget to prefix your text file with the default assembly namespace):

private static void Usage()
{
    using (
        Stream s =
            Assembly.GetExecutingAssembly().GetManifestResourceStream(
                "Sneal.Build.ConfigPoke.Console.usage.txt"))
    {
        using (StreamReader reader = new StreamReader(s))
        {
            System.Console.WriteLine(reader.ReadToEnd());
        }
    }
}
Wednesday, January 09, 2008 6:51:36 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Monday, January 07, 2008

I have no proof of this other than my changes weren't showing up, but it seems like MSBuild project files that are directly imported into a project are cached in Visual Studio between builds.  I had to make changes to my imported build script, exit Visual Studio, then restart VS before my changes would take affect.  Luckily if you build from the command line this doesn't happen.

Monday, January 07, 2008 8:45:28 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 

I often need to refer to tools and libraries in my SVN repository that live in a folder not underneath the current project folder.  For instance I may need to use NUnit from trunk\store\web, but NUnit lives under trunk\tools\nunit\bin. 

Relative to the trunk, I know where to find NUnit, but where's the trunk?  The trunk may be checked out to c:\source\mytrunk or e:\work, or even the user's documents and settings folder.  This means I must use relative paths to NUnit from the current project directory, but this gets tricky if I have an external MSBuild target file that is shared between projects.

The only way to tell the shared imported targets file where things are relative to it, is to have the parent MSBuild script to pass in a property telling it where the root of the source control folder is (or where the tools directory is).  In my case this is the 'SourceDirectory' property which should point to the root of my source control folder hierarchy.  This gets annoying after a while since everywhere I include the shared targets file I first must create the SourceDirectory property first, like so:

<PropertyGroup>
  <SourceDirectory>$(MSBuildProjectDirectory)\..\..</SourceDirectory>
</PropertyGroup>

 

Yes this works pretty well, but it makes the shared imported targets file not-so-self contained and I have to repeat this over and over again in every project file that wants to include the shared targets files, which in my case is every single csproj file.  I have to do this since my project files live at varying depths in the folder hierarchy.  Instead of specifying this property everywhere, I've put this at the top of my shared targets file:

<PropertyGroup Condition="'$(SourceDirectory)' == ''">
  <SourceDirectory Condition="'$(SourceDirectory)' == '' And Exists('$(MSBuildProjectDirectory)\Master.proj')">$(MSBuildProjectDirectory)</SourceDirectory>
  <SourceDirectory Condition="'$(SourceDirectory)' == '' And Exists('$(MSBuildProjectDirectory)\..\Master.proj')">$(MSBuildProjectDirectory)\..</SourceDirectory>
  <SourceDirectory Condition="'$(SourceDirectory)' == '' And Exists('$(MSBuildProjectDirectory)\..\..\Master.proj')">$(MSBuildProjectDirectory)\..\..</SourceDirectory>
  <SourceDirectory Condition="'$(SourceDirectory)' == '' And Exists('$(MSBuildProjectDirectory)\..\..\..\Master.proj')">$(MSBuildProjectDirectory)\..\..\..</SourceDirectory>
  <SourceDirectory Condition="'$(SourceDirectory)' == '' And Exists('$(MSBuildProjectDirectory)\..\..\..\..\Master.proj')">$(MSBuildProjectDirectory)\..\..\..\..</SourceDirectory>
  <SourceDirectory Condition="'$(SourceDirectory)' == '' And Exists('$(MSBuildProjectDirectory)\..\..\..\..\..\Master.proj')">$(MSBuildProjectDirectory)\..\..\..\..\..</SourceDirectory>
  <SourceDirectory Condition="'$(SourceDirectory)' == '' And Exists('$(MSBuildProjectDirectory)\..\..\..\..\..\..\Master.proj')">$(MSBuildProjectDirectory)\..\..\..\..\..\..</SourceDirectory>
  <SourceDirectory Condition="'$(SourceDirectory)' == '' And Exists('$(MSBuildProjectDirectory)\..\..\..\..\..\..\..\Master.proj')">$(MSBuildProjectDirectory)\..\..\..\..\..\..\..</SourceDirectory>
  <SourceDirectory Condition="'$(SourceDirectory)' == '' And Exists('$(MSBuildProjectDirectory)\..\..\..\..\..\..\..\..\Master.proj')">$(MSBuildProjectDirectory)\..\..\..\..\..\..\..\..</SourceDirectory>
</PropertyGroup>

 

What this does is look for a Master.proj file in each directory traversing upwards until it finds one, at which point the SourceDirectory property becomes set.  I can do this since I know that I only ever have one Master.proj file in the root of my SVN repository, and I never have a project more than 4 folders deep (although here I support 9 levels)  It's kind of cheesy, but it works well and saves me from having to do anything other than just import the shared targets file.

<Import Project="$(MSBuildProjectDirectory)\..\..\utils\gp.vs.targets" />

 

Hopefully Microsoft will add a well known property that allows you to get the current directory of the current build file, rather than just the parent script directory like MSBuildProjectDirectory does now.

Monday, January 07, 2008 7:46:42 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Saturday, January 05, 2008

Environment specific configuration can be a real pain to manage effectively.  I think I've tried every approach imaginable and it seems there is no perfect solution.  I just switched my many environment specific configuration files (I actually got to delete ~25 config files from SVN!) over to a handful of Windsor configuration files that use conditional elements and includes like so:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <properties>
    <?if LIVE?>
      <billingGatewayHost>webserver1</billingGatewayHost>
      <sessionConnectionString>server=dbserver1;database=ASPStateNET2;integrated security=SSPI</sessionConnectionString>
    <?elseif TEST?>
      <billingGatewayHost>webserver3</billingGatewayHost>
      <sessionConnectionString>server=dbserver3;database=ASPStateNET2;integrated security=SSPI</sessionConnectionString>
    <?end?>
  </properties>
</configuration>

 

I had so many configuration files because I had 5 environments, plus a session state, http module, appSettings, and some other stuff broken out into separate files using the standard .NET include section file mechanism.

I think I like the conditionals better than separate files for each environment which requires a postbuild process either at build or deployment time to merge or copy XML.  Also, it should be pretty obvious to any developer what's going on here.  Unfortunately the standard ASP.NET configuration mechanism doesn't have anything like this, and what's worse is that some components - like session state as in my case - can only be configured through the web.config.

That means no overriding the configuration at runtime or using Windsor.  No, the internal SqlSessionStateStore class in the FCL reads directly from the web.config using an internal static helper class.  This means no inheritance or injecting my configuration, even with the help of reflection.

ASP.NET does support custom session state providers, however I just want to use the SqlSessionStateStore class with a different configuration mechanism (I would really like to wire the thing up using Windsor); and creating a brand new provider is a lot of work.

Since all of my properties (i.e. connection strings) are in an external Windsor configuration file, it makes the most sense to also put my conditional session state connection strings in there as well (if you haven't picked up on it yet, I'm using SQL Server to store my session data, hence the SQL connection string for session).

Here's how I ended up getting the SqlSessionStateStore to use the connection string from my Windsor config...  And here is the hack in the global.asax.cs... 

private static volatile bool appDomainIsShuttingDown = false;
 
public override void Application_Start(Object sender, EventArgs e)
{
    base.Application_Start(sender, e);
 
    if (IoC.Resolve<SessionStateConnectionStringSetter>().SyncWebConfigSettingIfDifferent())
    {
        HttpRuntime.UnloadAppDomain();
        appDomainIsShuttingDown = true;
    }
}

 

On application start my base Application_Start method gets my DI container up and running.  After that I ask the container for a SessionStateConnectionStringSetter instance which is injected with the proper session DB connection string from the container.  All this class does is modify the web.config if the connection string it has differs from the one in the web.config.  After that, if the web.config has been modified, the current app domain is shutdown and the appDomainIsShuttingDown flag is set to true. 

In case you're not sure, static variables are scoped to the current app domain by default, so the next request for the web app will have the appDomainIsShuttingDown variable set to false (it's default value).  I think there is a threading bug in there if multiple application start events get fired off simultaneously, so I should probably add a lock around the web.config update.  I'll have to look into that.  Anyway, here's the class that updates the web.config with the new session state connection string.

public class SessionStateConnectionStringSetter
{
    private readonly string sessionStateConnStr;
 
    public SessionStateConnectionStringSetter(string sessionConnectionString)
    {
        this.sessionStateConnStr = sessionConnectionString;
    }
 
    public bool SyncWebConfigSettingIfDifferent()
    {
        ExeConfigurationFileMap map = new ExeConfigurationFileMap();
        map.ExeConfigFilename = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "web.config");
 
        Configuration config = ConfigurationManager.OpenMappedExeConfiguration(map, ConfigurationUserLevel.None);
        SessionStateSection session = (SessionStateSection)config.GetSection("system.web/sessionState");
 
        if (session.SqlConnectionString != sessionStateConnStr)
        {
            session.SqlConnectionString = sessionStateConnStr;
            config.Save();
            return true;
        }
 
        return false;
    }
}

 

Further along in the ASP.NET pipeline the Application_BeginRequest method executes and the current user request (the one that initiated the web.config to be updated in the first place) is redirected - back to the same page they asked for originally.  This is done so the user doesn't see a nasty exception that will likely happen if processing continues with the original incorrect session DB connection string.

protected void Application_BeginRequest(object sender, EventArgs e)
{
    // This will cause the user to re-request the page using a new appdomain
    if (appDomainIsShuttingDown)
        Response.Redirect(Request.Url.ToString());
}

 

The thing to note about all this, is that the web.config update should only ever happen on the very first request for the application after a deployment, which not too surprisingly will almost always be the person doing the deployment (assuming they check their work). 

I wish I could find a better way to do this, but as far as I can tell there's no other way to set this programmatically at runtime.  I may just break down and add a special case to my NSIS installer to update the web.config session connection string from my Windsor configuration.  I really just wanted to see if I could get this to work without a build or deploy step...

Saturday, January 05, 2008 9:25:15 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, January 04, 2008

For 15 minutes today I couldn't figure out why my Windsor configuration was blowing up with a mysterious and unrevealing error: "deserialize element cannot process element component."  This happened after I moved some configuration out of my main windor config to a new embedded resource config file. I thought is this a bug in Windsor?  After briefly looking through the Windsor test source it was pretty obvious that this is supported and tested.

It turns out that I forgot to include the "components" tag in my config file.  Argh!  The answer was right in front of me all this time, I just didn't see it - even after comparing my broken config to a working config.  I'm glad I'm not the only one to have this problem...

Friday, January 04, 2008 7:01:38 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [2]  |