Loren Halvorson's Blog

If your only tool is a hammer...

September 2004 - Posts

C# 101 - Representing a double quote in string literals

I'm sure almost every C# developer already knew this, but I thought a post might help the few that didn't. I had always wondered how it was done and stumbled across it yesterday buried in an example in the C# Language Specification.

If you want to represent a double quote in a string literal, the escape sequence is two double quotes.

string myString = @"<xml attribute=""my attribute""/>";

I have found this useful for storing nicely formatted XML fragments in constants without resorting to 1) putting it all on one long line without string literals or 2) loading from a file or resource or 3) concatenating at run time, or 4) switching to single quotes.

  private const string requestXml =
@"<?xml version=""1.0"" encoding=""utf-8""?>
<soap:Envelope
 xmlns:xsi=""
http://www.w3.org/2001/XMLSchema-instance""
 xmlns:xsd=""
http://www.w3.org/2001/XMLSchema""
 xmlns:soap=""
http://schemas.xmlsoap.org/soap/envelope/"">
 <soap:Body>
  <ForceBuild xmlns=""
http://tempuri.org/"">
   <projectName><![CDATA[{0}]]></projectName>
  </ForceBuild>
 </soap:Body>
</soap:Envelope>";

Now I know.

Invoking different NAnt targets on different versions of Windows

A friend just asked me how to invoke different targets for different versions of Windows using NAnt 0.84. At first I told him to start using NAnt 0.85 so he could use the new string comparison functions, but as I thought about it more, I realized that it's not hard to do without resorting to functions, the following will work fine in either version of NAnt:

<project name="test" default="go">

  <target name="go">
    <readregistry
     
property="os.version"
     
key="SOFTWARE\Microsoft\Windows NT\CurrentVersion\CurrentVersion"
      hive="LocalMachine" />

    <call target="dothis.${os.version}"/>
  </target>

  <target name="dothis.5.0">
    <echo message="Hello Windows 2000"/>
  </target>

  <target name="dothis.5.1">
    <echo message="Hello Windows XP"/>
  </target>

  <target name="dothis.5.2">
    <echo message="Hello Windows 2003"/>
  </target>

</project>

Simple.

A better way to set the version into the VersionInfo.cs file

In my previous post I mentioned that I had written my own <setversion> task to set the file version number in the centralized VersionInfo.cs. I took another look at NAnt's built in tasks, and <asminfo> does exactly what I need. I just changed the NAnt build script to write out VersionInfo.cs like this:

  <asminfo output="VersionInfo.cs" language="CSharp">
    <imports>
      <import name="System.Reflection" />
    </imports>
    <attributes>
      <attribute type="AssemblyFileVersionAttribute" value="${label-to-apply}" />
    </attributes>
  </asminfo>

Which writes out:

using System.Reflection;

//------------------------------------------------------------------------------
// <autogenerated>
//     This code was generated by a tool.
//     Runtime Version: 1.1.4322.573
//
//     Changes to this file may cause incorrect behavior and will be lost if
//     the code is regenerated.
// </autogenerated>
//------------------------------------------------------------------------------

[assembly: AssemblyFileVersionAttribute("1.0.0.123")]

Perfect. I love throwing away my own custom code in favor of built-in stuff!

Versioning Groups of Assemblies in sync

We often build groups of assemblies that all need to be marked with the same build number. Rather than having the build check out a whole bunch of AssemblyInfo.cs files from source control, set their file versions, and check them in, we use Visual Studio .NET's handy and (not widely known) Linked file feature to centralize the version information into a single file. Then the build only has to worry about updating the build number in one place. Here's how we do it.

Create a VersionInfo.cs file in a central place in your source tree.

---VersionInfo.cs---
using System.Reflection;
using System.Runtime.CompilerServices;

[assembly: AssemblyFileVersion("1.0.0.123")]

In each of the projects that need to be versioned in sync, do the following:

  1. Remove the AssemblyFileVersion attribute from AssemblyInfo.cs so you don't have duplicates.
  2. Select Add Existing Item... to the project
  3. In the file-open dialog navigate up to the VersionInfo.cs file and click the little down arrow on the right side of the Open button and select Link File

This will write the following into your csproj file.

---MyProject1.csproj---

<VisualStudioProject>
    <CSHARP>
        <Files>
            <Include>
                <File
                    RelPath = "AssemblyInfo.cs"
                    SubType = "Code"
                    BuildAction = "Compile"
                />
                <File
                    RelPath = "VersionInfo.cs"
                    Link = "..\VersionInfo.cs"
                    SubType = "Code"
                    BuildAction = "Compile"
                />


We use CruiseControl.NET, NAnt and NAntContrib's Visual SourceSafe tasks in our build process. Centralizing the versioning in a single VersionInfo.cs simplifies our build process and guarantees that all assemblies built at the same time will be marked with the same build number.

I did have to write a custom NAnt task that sets the version into the VersionInfo.cs file using a regular expression search and replace. The custom task is called like this:

<!-- label-to-apply property provided by CruiseControl.NET -->
<property name="label-to-apply" value="1.0.0.2"/>
:
<setversion file="VersionInfo.cs" fileVersion="${label-to-apply}"/>

Because I didn't find anything in NAnt or NAntContrib that did anything this simple for me, but maybe I just missed it. Anyway, this system is working quite nicely for us.

Forehead slapper on DateTime.AddDays()

I should have read the documentation for DateTime.AddDays() a little more carefully. This one caused a minor bug for me this week:

DateTime dt = DateTime.Now;
dt.AddDays(1);

What's in dt after the call to AddDays()? Well I assumed it would be this time tomorrow. However, from the .NET documentation for AddDays() I finally noticed: "This method does not change the value of this DateTime. Instead, a new DateTime is returned whose value is the result of this operation." Doh!

I should have written

DateTime dt = DateTime.Now;
dt = dt.AddDays(1);

That's what I get for not reading the docs, and instead assuming from the function's name that I know what it does (one of the downsides of Intellisense is it almost makes finding functions too easy). Although I'm not sure what would I have called the function AddDays() so its behavior would be more clear. Maybe something like: DateTime.ReturnNewDateTimeWithThisNumberOfDaysAddedToIt()...nah maybe not.

 

More Posts