Binding Redirects, StructureMap and Dependency Version Upgrades

Dealing with the fallout in failing unit tests from a code merge is one of the most frustrating tasks in software development.  And as one of a (very) small number of developers on our team that believes in unit testing, it fell to me to determine the cause of multiple instances of the structuremap exception code 207 error.

As it turned out, the culprit was a tactic I’ve used in the past to work with code that only works with a specific version of an assembly–the binding redirect.  When the same person is in charge of upgrading dependencies, this tends not to be an issue because if they’ve used binding redirects, they know it’s necessary to update them when dependencies are upgraded.    In this case, the dependencies were upgraded and the redirects were not.  As a result, StructureMap tried to find a specific version of an assembly that was no longer available and threw exception code 207 when it failed.

Replicating Folder Structures in New Environments with MSBuild

I recently received the task of modifying an existing MSBuild script to copy configuration files from one location to another while preserving all but the top levels of their original folder structure.  Completing this task required a refresher in MSBuild well-known metadata and task batching (among other things), so I’m recounting my process here for future reference.

The config files that needed copying were already collected into an item via a CreateItem task.  Since we’re using MSBuild 4.0 though, I replaced it with the simpler ItemGroup.  CreateItem has been deprecated for awhile, but can still be used.  There is a bit of debate over the precise differences between CreateItem and ItemGroup, but for me the bottom line is the same (or superior) functionality with less XML.

Creating a new folder on the fly is easy enough with the MakeDir task.  There’s no need to manually check whether or not the directory you’re trying to create already exists or not.  The task just works.

The trickiest part of  this task was figuring out what combination of well-known metadata needed to go in the DestinationFiles attribute of the Copy task to achieve the desired result.  The answer ended up looking like this:

<Copy SourceFiles="@(ConfigFiles)" DestinationFiles="$(OutDir)_Config\$(Environment)\%(ConfigFiles.RecursiveDir)%(ConfigFiles.Filename)%(ConfigFiles.Extension)" />

The key bit of metadata is the RecursiveDir part.  Since the ItemGroup that builds the file collection uses the ** wildcard, and it covered all the original folder structure I needed, putting after the new “root” destination and before the file names gave me the result I wanted.  Another reason that well-known metadata was vital to the task is that all the files have the same name (Web.config), so the easiest way to differentiate them for the purpose of copying was their location in the folder structure.

In addition to the links above, this book by Sayed Ibrahim Hashimi was very helpful.  In a previous job where configuration management was a much larger part of my role, I referred to it (and sedodream.com) on a daily basis.

Visual Studio & TFS Behavior Tweaks

One of a few long-running annoyances I’ve had with every version of TFS is one of the default behaviors on check-in. The default is to resolve an open item on check-in, which is virtually never the case the first (or second, or third, etc) time you check in code to resolve a bug or implement new functionality. Fortunately, Edsquared has the solution.

After making this long-overdue change in my development environment, I exported the keys for VS2010 and VS2012 as registration entry files below:

Feel free to use them in your environment.

The Perils of Renaming in TFS

Apparently, renaming an assembly is a bad idea when TFS is your version control system.

Earlier this week, one of my co-workers renamed an assembly to consolidate some functionality in our application yesterday, and even though TFS said the changes were checked in, they weren’t.

I got the latest code the morning after the change, and got nothing but build failures. We’re using the latest version of TFS and it’s very frustrating that something like this still doesn’t work properly.

Ultimately, the solution was found at the bottom of this thread.

The only way I’ve found to avoid this kind of hassle is to create a new assembly, copy your code from the old assembly to the new one, change any references to the old assembly to use the new assembly, then delete the old assembly once you’ve verified the new one is working.

Deleting TFS Tasks

I changed the state of a TFS task I was working on recently, only to discover the workflow wouldn’t let me return it to it’s prior state.  Until today, I didn’t know it was possible to delete TFS tasks if you made a mistake in changing one.  But some Googling revealed a blog post that explained how to delete tasks.

The direction of the slashes for the URL can point forward (/) instead of backward () and the witadmin.exe destroywi /Collection:<TFS url> /id:<Task id> command still works.

Calling MSTest from MSBuild or The Price of Not Buying TFS

When one of my colleagues left for a new opportunity, I inherited the continuous build setup he built for our project.  This has meant spending the past few weeks scrambling to get up to speed on CruiseControl.NET, MSTest and Subversion (among other things).  Because we don’t use TFS, creating a build server required us to install Visual Studio 2008 in order to run unit tests as part of the build, along with a number of other third-party tasks to make MSBuild work more like NAnt.  So the first time a build failed because of tests that had passed locally, I wasn’t looking forward to figuring out precisely which of these pieces triggered the problem.

After reimplementing unit tests a couple of different ways and still getting the same results (tests passing locally and failing on the build server), we eventually discovered that the problem was a bug in Visual Studio 2008 SP1.  Once we installed the hotfix, our unit tests passed on the build server without us having to change them.  This hasn’t been the last issue we’ve had with our “TFS-lite” build server.

Build timeouts have proven to be the latest hassle.  Instead of the tests passing locally and failing on the build server, they actually passed in both places.  But for whatever reason, the test task didn’t really complete and build timed out.  Increasing the build timeout didn’t address the issue either.  Yesterday, thanks to the Microsoft Build Sidekick editor, we narrowed the problem down to the MSTest task in our build file.  The task is the creation of Nati Dobkin, and it made writing the test build target easier (at least until we couldn’t get it to work consistently).  So far, I haven’t found (or written) an alternative task, but I did find a blog post that pointed the way to our current solution.

The solution:

<!– MSTest won’t work if the tests weren’t built in the Debug configuration –>
<Target Name=”Test:MSTest” Condition=” ‘$(Configuration)’ == ‘Debug'”>
<MakeDir Directories=”$(TestResultsDir)” />
<MSBuild.ExtensionPack.FileSystem.Folder TaskAction=”RemoveContent” Path=”$(TestResultsDir)” />

<Exec Command=””$(VS90COMNTOOLS)..IDEmstest.exe” /testcontainer:$(TestDir)<test assembly directory>bin$(Configuration)<test assembly>.dll /testcontainer:$(TestDir)<test assembly directory>bin$(Configuration)<test assembly>.dll /testcontainer:$(TestDir)<test assembly directory>bin$(Configuration)<test assembly>.dll /runconfig:localtestrun.testrunconfig” />

</Target>

TestDir and TestResultsDir are defined in a property group at the beginning of the MSBuild file.  VS90COMNTOOLS is an environment variable created during the install of Visual Studio 2008.  Configuration comes from the solution file.  Actual test assembly directories and names have been replaced  with <test assembly> and <test assembly directory>.  The only drawback to the solution so far is that we’ll have to update our MSBuild file if we add a new test assembly.