There are a couple of things that i strongly dislike about the Web.config transformation in VS2010:
- Only works with XML files (eg: Can’t be used to generate a release notes.txt file)
Does not seem to support externalized sections, eg: log4net.config in a separate file
No support to copy/paste transform files
- Only works when Visual Studio 2010 is installed (And i am still not convinced a build server should have this).
- Ties environment to build configuration
Lesson learned: Don’t trust your co-workers, always double-check!
- Having multiple transformations is easy-peasy, just invoke the TransformXml task for all your config files and make sure your transformation files are correct. For log4net this would look like:
<log4net xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<root>
<level value="ERROR" xdt:Transform="Replace"/>
</root>
</log4net>
The support for copy/paste can be achieved by removing the DependentUpon tag in your proj file (At the cost that you don’t have the + sign in solution explorer which ‘hides’ the transforms files)
One of the disadvantages of the TemplateFile task (msbuildtasks) is the fact that it requires a lot of typing to define template values:
<ItemGroup Condition= " '$(ConfigurationEnvironment)'=='build' ">
<Tokens Include="a">
<ReplacementValue>localhost</ReplacementValue>
</Tokens>
<Tokens Include="b">
<ReplacementValue><mynode/></ReplacementValue>
</Tokens>
</ItemGroup>
Here is a format proposition to make this a lot more finger friendly:
<configuration>
<variables env="build">
<x name="a">localhost</x>
<x name="b><mynode/></x>
</variables>
</configuration>
Here is the msbuild script we need to achieve that:
<PropertyGroup>
<ConfigurationFile>configuration.xml</ConfigurationFile>
<ConfigurationEnvironment>build</ConfigurationEnvironment>
</PropertyGroup>
<!-- Retreive all template values for the specific environment -->
<XmlQuery XmlFileName="$(ConfigurationFile)" XPath = "//variables[@env='$(ConfigurationEnvironment)']/*">
<Output TaskParameter="Values" ItemName="Values" />
</XmlQuery>
<!-- Construct @Tokens -->
<ItemGroup>
<Tokens Include="%(Values.name)">
<ReplacementValue>%(Values._innerxml)</ReplacementValue>
</Tokens>
</ItemGroup>
<!-- Generate the configuration files -->
<Message Text="Available variables:" />
<Message Text="====================" />
<Message Text="%(Tokens.Identity): %(Tokens.ReplacementValue)" />
Happy coding!
A while ago i wrote about a Clever TemplateFile hack to use some xml block as ReplacementValue. Today i realized there is a clean way to achieve this by defining the value as CDATA:
<TemplateTokens Include="mex">
<ReplacementValue>
<![CDATA[<endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />]]>
</ReplacementValue>
</TemplateTokens>
Ship It!: A Practical Guide to Successful Software Projects is focussed on one core idea: “The List”. The place in which you track your (and your teams) to-do and done work. I do not agree with everything written in the book but would still want to recommend it.
Behind Closed Doors: Secrets of Great Management first let’s you look behind the closed doors of all those private meetings and then provides you a set of techniques that allow you to be(come) a better manager. All 160 pages were worth reading!
Brownfield Application Development in .NET is one of the better books i have read recently. The first part is about the ecosystem (version control, automated build, continuous integration, testing, defect management, … ), the second part is about the code (OO-principles, layering, dependencies, ui patterns, …) and the last chapter talks about ways to maintain the momentum. Very much recommended!
Here is a quick reminder about the workings of Type.IsAssignableFrom:
class Fruit {}
class Banana : Fruit {}
[Test]
public void CanAssignBananaToFruit()
{
var fruit = typeof (Fruit);
var banana = typeof (Banana);
Assert.IsTrue(fruit.IsAssignableFrom(banana));
}
[Test]
public void CanNotAssignFruitToBanana()
{
var fruit = typeof(Fruit);
var banana = typeof(Banana);
Assert.IsFalse(banana.IsAssignableFrom(fruit));
}
I really hate this API because it always seems backward to me. Here is how i really want to use it:
Assert.IsTrue(banana.CanBeAssignedTo(fruit));
Assert.IsFalse(fruit.CanBeAssignedTo(banana));
With the aid of an extension method we can easily achieve this:
public static bool CanBeAssignedTo(this Type sourceType, Type destinationType)
{
return destinationType.IsAssignableFrom(sourceType);
}
Last week i noticed the following post from Scott Hanselman: Removing Dead Tracks (Duplicates that don’t exist) from iTunes using C#. As a good boy scout i noticed that these days iTunesLib.IITTrackCollection inherits from IEnumerable so i rewrote the code a little:
class Program
{
[STAThread]
static void Main()
{
var itunes = new iTunesApp();
itunes.DeleteTracksThatDoNotExist();
}
}
public static class ITunesExtensions
{
public static void DeleteTracksThatDoNotExist(this IiTunes itunes)
{
var tracksThatDoNotExist = FindTracksThatDoNotExist(itunes);
foreach (var track in tracksThatDoNotExist) track.Delete();
}
public static IEnumerable<IITFileOrCDTrack> FindTracksThatDoNotExist(this IiTunes iTunes)
{
return iTunes.LibraryPlaylist.Tracks
.OfType<IITFileOrCDTrack>()
.Where(track => !File.Exists(track.Location));
}
}
97 Things Every Programmer Should Know was the last book that i wanted to read at the pool during summer holidays. As with all 97 things books you get a lot of valuable tips but i found this book not as exciting as the previous ones…
Not going to waste time re-iterating what’s in the book because you can find that here: Implementing Lean Software Development: From Concept to Cash. Just make sure that you get your copy and read it from beginning to end. Afterwards you may want to hand it over to your manager