# 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]  | 
# Thursday, January 03, 2008

For Christmas my wife got me a new Zune 8GB, which I'm really enjoying so far.  I think Microsoft has done a really good job putting the newer Zune's together.  Not only is the UI pleasant, but the thing light weight and easy to use, which is in sharp contrast to the older hard drive based devices.  Originally my wife got me a 16GB SanDisk Sansa View, which would constantly freeze up, even after a firmware Flash.  I just wanted something that works, so I got a Zune.

Now that I can listen to podcasts on the commute to and from work, I've done a little bit of searching and thought I would try out the following podcasts:

  • DotNetRocks
  • Hanselminutes
  • Polymorphic Podcast
  • FLOSS Weekly
  • Agile Toolkit Podcast

I got these from these Ayende and Jeffrey Palermo's blog posts and comments.

As a side note I stumbled upon what Ayende Rahein means: Freedom Dawn.

Thursday, January 03, 2008 11:07:40 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 

I needed to make a change to some existing legacy* JavaScript that validates credit card numbers in the browser.  For the most part the project was green field except for a few services, so overall test coverage was very good - except for the JavaScript code.  The lack of test coverage in the increasingly growing JavaScript libraries was starting to really bother me, so I set out to find a way to create "unit" tests for my JavaScript code that didn't require any setup or any extra steps for another developer to run.  What I really was wanting, was for my JS tests to be integrated and run along side my NUnit tests.

After some searching it appeared that JsUnit looked to be the most widely used and supported JavaScript testing facility available.  The one problem with this is that it requires you run the tests through a frame in a browser, which is a whole lot slower and less integrated with our NUnit tests that I wanted.

To get faster feedback on my JS tests I immediately thought of using WatiN to drive the JsUnit tests.  After a quick Google search I found that Adam Esterline had already done something very similar.  The main difference between what he's done and what I wanted was that I wanted JsUnit errors results right there in my ReSharper test runner window and I wanted my test scripts to be pure JavaScript rather than HTML files that JsUnit uses (because I wanted to keep the SOC high, and be able to dynamically change the location of JsUnit relative to my test scripts).  Like Adam, I'm auto generating a test HTML page for JsUnit and using WatiN to drive IE (although since I get my results outside of IE, I'm thinking I can drop using WatiN altogether for this and make it browser independent).

The workflow for using the JsUnit utility library is as follows:

  1. In your test project include a reference to JsUnit.Utils.dll
  2. Create a folder (or two) to contain your JavaScript JsUnit test fixtures.
  3. Create your JsUnit test fixtures as separate .js files.
  4. Create a .NET NUnit test fixture with a test that runs the JsUnitTestRunner from JsUnit.Utils.

Here's my solution explorer window with the JsUnit.Utils reference and the c# and the JavaScript fixtures.

JsUnitSlnExplorer

Once that is setup you create a single NUnit test that kicks off all the JsUnit tests.

/// <summary>
/// This runs all JsUnit tests found in the JavaScript folder and makes
/// their results available through NUnit (if any fail).
/// </summary>
[TestFixture]
public class JavaScriptFixture
{
    #region Setup/Teardown
 
    [SetUp]
    public void SetUp()
    {
        jScriptTestDir = AppDomain.CurrentDomain.BaseDirectory + "\\..\\..\\JavaScript\\";
        jsUnitDir = ConfigurationManager.AppSettings["JsUnitDir"];
    }
 
    #endregion
 
    private string jScriptTestDir;
    private string jsUnitDir;
 
    [Test]
    public void RunAllJSUnitTests()
    {
        Assert.IsTrue(Directory.Exists(jScriptTestDir),
                      string.Format("Could not find the JsUnit tests directory: '{0}'", jScriptTestDir));
 
        Assert.IsTrue(Directory.Exists(jsUnitDir),
                      string.Format("Could not find the JsUnit directory: '{0}'", jsUnitDir));
 
        ITestFileReader reader = new SuffixTestFileReader(".js", new TestFileReader(jScriptTestDir));
        new JsUnitTestRunner(reader, jsUnitDir).Run();
    }
}

 

Running this test through the ReSharper Test Runner runs all my JsUnit tests, so when I choose "Run All Tests from Solution," it really means "run all tests".  More importantly, if any JsUnit tests fail during execution the JsUnit error is displayed in the output window.  Here's my "testShouldValidateCreditCard" test that is failing which shows the JsUnit error text:

JsUnitInsideNunit

If you've ever used JsUnit before I bet you're wonder how I'm getting the result back, since JsUnit is entirely web based and only supports posting results to a URL (generally for use with the JsUnitServer).  What I'm doing is unpacking an ashx file that is an embedded resource, copying the current running DLL (JsUnit.Utils.dll) to the bin directory under the current running directory, and then starting the .NET 2 webserver.webdev.exe to listen for results using the unpacked ashx file.  The results are then saved out to a predetermined text file which JsUnitTestRunner is looking for and then parses out any errors.  I could use .NET remoting or something fancier, but this works well enough for now.

There's still a lot that could be done to clean this up, like removing the WatiN dependency, adding new features and so on.  There are also a few bugs I need to work out, but so far so good.

*legacy in the sense that there were no unit tests, even though the code is only a month old.

Thursday, January 03, 2008 12:00:20 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Friday, December 28, 2007

ReSharper 3.1 just came out, and of course I immediately installed it.  So far so good.  It fixed a couple of annoying crashes for me and added a new feature.

  • I can now open my MSBuild project files without ReSharper crashing constantly.
  • Visual Studio has yet to crash on exit.
  • And the best reason to upgrade: solution wide error analysis has been added.  Pic below.

ReSharperErrorsInSolution

Friday, December 28, 2007 2:17:52 AM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, December 27, 2007

Today at work we needed to compare the contents of a SQL table against an FTP directory.  We originally had a process that involved a lot of steps using Excel, NotePad, and command line FTP.

You could create a PowerShell script that iterates the SQL Server data and verifies each file is on the FTP server. This seemed like it would be slow so I opted for running FTP directly from my query (assuming you have xp_cmdshell enabled). Here's an example. Notice it's getting the FTP commands to run from the ftpoptions.txt file.

declare @FilesOnDisk table

(

[File] [nvarchar] (50)

)

 

insert into @FilesOnDisk ([File]) exec xp_cmdshell 'ftp -A -s:c:\temp\ftpoptions.txt ftp.microsoft.com'

 

select * from @FilesOnDisk where [File] is not null

 

Now we can join this temp table data to our local SQL Server table to find missing files on the FTP server...

Thursday, December 27, 2007 5:12:35 PM (GMT Standard Time, UTC+00:00)  #    Disclaimer  |  Comments [0]  |