Using multiple versions of the same namespace in the same Visual Studio project

I had an issue recently when using the NServiceBus.Testing library (version 2.6.0.1504, which is a bit outdated now, but the problem is still interesting). The NServiceBus.Testing library comes with the popular Rhino Mocks framework built in, so by adding a reference to NServiceBus.Testing you automatically end up with the Rhino.Mocks namespace being populated. Unfortunately, I was already using a later version of the Rhino Mocks framework in my project, so this caused a conflict. I couldn’t just remove the existing Rhino Mocks reference because I was relying on features that were not available in the version included with NServiceBus.Testing, and obviously I needed features from the NServiceBus.Testing library, so I had to find a way for the two to co-exist peacefully.

This is possible using reference aliases. By default, when you add a reference to a project, the namespaces in the reference are added with the global namespace alias. However, you can customise the alias by changing the properties of the reference, like so:

Reference alias

This stops the reference being added to the global namespace alias, which in this case meant that any references to the Rhino.Mocks namespace would resolve (correctly) to the Rhino.Mocks reference, instead of NServiceBus.Testing. When you want to use namespaces from a custom-aliased reference in a class, you need to specify the custom alias along with the extern keyword at the top, and use the alias along with a double colon :: as a prefix in the using import statements to specify where to look for a particular namespace, e.g.

extern alias NServiceBusTest;
...
using NServiceBusTest::NServiceBus.Testing;
using Rhino.Mocks;
...
namespace Tests 
{
    [TestFixture]
    public class TestClass
    {
        private Saga<MySaga> _saga;
        private ISomeDependency _dependency;

        [SetUp]
        public void CreateSaga()
        {
            _dependency = MockRepository.GenerateMock<ISomeDependency>(); // resolves to the actual Rhino Mocks reference
            _saga = Test.Saga<MySaga>(); // comes from the NServiceBus testing framework
        }
    }
}

Job’s a good ‘un.

Nested elements in custom NAnt tasks

There’s a great post here detailing how to add nested elements within a custom NAnt task. I was writing a task to create IIS websites, and wanted to be able to specify multiple bindings, for example:

<IIS.Website.Create name="MySite" physicalPath="path">
    <bindings>
        <binding type="http" ip="1.2.3.4" port="8080" host="domain.com" />
        <binding type="net.msmq" host="localhost" />
    </bindings>
</IIS.Website.Create>

The important NAnt attributes you’ll need to know about are:

  • BuildElement – denotes a property as representing a nested element within a task, e.g. the <bindings> element above
  • BuildElementArray – denotes a property as representing an array of nested elements, e.g. the set of <binding> elements above
  • ElementName – used to denote a class as representing a build XML element

and the relevant NAnt classes are:

  • DataTypeBase – provides the abstract base class for NAnt types
  • Element – represents a build XML element

In simplified summary, to allow the XML snippet shown at the beginning of this post to be used, you will need to:

  • Create a class to represent the <binding> element, which derives from Element and is attributed with ElementName:
    [ElementName("binding")]
    public class IisBinding : Element
    {
        [TaskAttribute("type")]
        public string Type { get; set; }
    
        [TaskAttribute("ip")]
        public string IpAddress { get; set; }
    
        [TaskAttribute("port")]
        public int Port { get; set; }
    
        [TaskAttribute("host")]
        public string Host { get; set; }
    }
    
  • Create a data type that will represent a set of those <binding> elements. The crucial part is the BindingElements array. You’ll note that this class creates a representation of the IIS bindings (IisBindingDescription) from the elements in the array – you might just use the elements directly, depending on the scenario.
    [ElementName("bindings")]
    public class IisBindingSet : DataTypeBase
    {
        private readonly IList<IisBindingDescription> _bindings = new List<IisBindingDescription>();
    
        public IList<IisBindingDescription> Bindings
        {
            get { return _bindings; }
        }
    
        [BuildElementArray("binding")]
        public IisBinding[] BindingElements
        {
            set
            {
                foreach (var binding in value)
                {
                    ParseBinding(binding);
                }
            }
        }
    
        private void ParseBinding(IisBinding binding)
        {
            ...
    
            _bindings.Add(new IisBindingDescription(type, bindingInformation));
        }
    }
    
  • N.B. for completeness’ sake, this is the IisBindingDescription class – it’s just a DTO.
    public class IisBindingDescription
    {
        public string Protocol { get; private set; }
        public string Information { get; private set; }
    
        public IisBindingDescription(string protocol, string information)
        {
            Protocol = protocol;
            Information = information;
        }
    }
    
  • Add a property to your task with a BuildElement attribute, which unfortunately must also specify the name for the data type element – this must match the name used in the ElementName attribute used when creating the data type.
    [BuildElement("bindings")]
    public IisBindingSet Bindings { get; set; }
    

That’s all there is to it! Now, if you used the XML snippet from the top of this post in your build file, you would end up with two IisBindingDescription objects available in the Bindings property (and two elements in the BindingElements property, if you wanted to use them directly) of the IisBindingSet, which can be used where required.

It should be possible to use the BuildElementArray directly in your Task class, if you didn’t want a surrounding type. Also, there is a BuildElementCollection attribute, which I imagine acts very similarly to BuildElementArray but allows you to use a collection type instead.

Building .NET 4.0 applications with NAnt

You need to add a new framework configuration which (still?) doesn’t come by default with the download of NAnt. Insert the following into your NAnt.exe.config file (alongside the similar looking framework configurations):

<framework
  name="net-4.0"
  family="net"
  version="4.0"
  vendor="Microsoft"
  description="Microsoft .NET Framework 4.0"
  sdkdirectory="${path::combine(sdkInstallRoot, 'bin')}"
  frameworkdirectory="${path::combine(installRoot, 'v4.0.30319')}"
  frameworkassemblydirectory="${path::combine(installRoot, 'v4.0.30319')}"
  clrversion="4.0.30319">
  <runtime>
    <probing-paths>
      <directory name="lib/net/2.0" />
      <directory name="lib/net/neutral" />
      <directory name="lib/common/2.0" />
      <directory name="lib/common/neutral" />
    </probing-paths>
    <modes>
      <strict>
        <environment>
          <variable name="COMPLUS_VERSION" value="v4.0.30319" />
        </environment>
      </strict>
    </modes>
  </runtime>
  <reference-assemblies basedir="${path::combine(installRoot, 'v4.0.30319')}">
    <include name="Accessibility.dll" />
    <include name="mscorlib.dll" />
    <include name="Microsoft.Build.Engine.dll" />
    <include name="Microsoft.Build.Framework.dll" />
    <include name="Microsoft.Build.Utilities.dll" />
    <include name="Microsoft.Vsa.dll" />
    <include name="Microsoft.VisualBasic.dll" />
    <include name="Microsoft.VisualBasic.Compatibility.dll" />
    <include name="Microsoft.VisualBasic.Compatibility.Data.dll" />
    <include name="System.Configuration.dll" />
    <include name="System.Configuration.Install.dll" />
    <include name="System.Data.dll" />
    <include name="System.Data.OracleClient.dll" />
    <include name="System.Data.SqlXml.dll" />
    <include name="System.Deployment.dll" />
    <include name="System.Design.dll" />
    <include name="System.DirectoryServices.dll" />
    <include name="System.dll" />
    <include name="System.Drawing.Design.dll" />
    <include name="System.Drawing.dll" />
    <include name="System.EnterpriseServices.dll" />
    <include name="System.Management.dll" />
    <include name="System.Messaging.dll" />
    <include name="System.Runtime.Remoting.dll" />
    <include name="System.Runtime.Serialization.Formatters.Soap.dll" />
    <include name="System.Security.dll" />
    <include name="System.ServiceProcess.dll" />
    <include name="System.Transactions.dll" />
    <include name="System.Web.dll" />
    <include name="System.Web.Mobile.dll" />
    <include name="System.Web.RegularExpressions.dll" />
    <include name="System.Web.Services.dll" />
    <include name="System.Windows.Forms.dll" />
    <include name="System.Xml.dll" />
  </reference-assemblies>
  <task-assemblies>
    <!-- include MS.NET version-neutral assemblies -->
    <include name="extensions/net/neutral/**/*.dll" />
    <!-- include MS.NET 2.0 specific assemblies -->
    <include name="extensions/net/2.0/**/*.dll" />
    <!-- include MS.NET specific task assembly -->
    <include name="NAnt.MSNetTasks.dll" />
    <!-- include MS.NET specific test assembly -->
    <include name="NAnt.MSNet.Tests.dll" />
    <!-- include .NET 2.0 specific assemblies -->
    <include name="extensions/common/2.0/**/*.dll" />
  </task-assemblies>
  <tool-paths>
    <directory name="${path::combine(sdkInstallRoot, 'bin')}"
    if="${property::exists('sdkInstallRoot')}" />
    <directory name="${path::combine(installRoot, 'v4.0.30319')}" />
    <directory name="${path::combine(installRoot, 'v2.0.50727')}" />
    <directory name="${path::combine(installRoot, 'v3.0')}" />
    <directory name="${path::combine(installRoot, 'v3.5')}" />
  </tool-paths>
  <project>
    <readregistry
    property="installRoot"
    key="SOFTWARE\Microsoft\.NETFramework\InstallRoot"
    hive="LocalMachine" />
    <readregistry
      property="sdkInstallRoot"
      key="SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.0A\WinSDKNetFxTools\InstallationFolder"
      hive="LocalMachine"
      failonerror="false" />
    <readregistry
      property="sdkInstallRoot"
      key="SOFTWARE\Microsoft\Microsoft SDKs\Windows\v6.1\WinSDK\InstallationFolder"
      hive="LocalMachine"
      failonerror="false" />
    <readregistry
      property="sdkInstallRoot"
      key="SOFTWARE\Microsoft\Microsoft SDKs\Windows\v7.0\WinSDK\InstallationFolder"
      hive="LocalMachine"
      failonerror="false" />
  </project>
  <tasks>
    <task name="csc">
      <attribute name="supportsnowarnlist">true</attribute>
      <attribute name="supportswarnaserrorlist">true</attribute>
      <attribute name="supportskeycontainer">true</attribute>
      <attribute name="supportskeyfile">true</attribute>
      <attribute name="supportsdelaysign">true</attribute>
      <attribute name="supportsplatform">true</attribute>
      <attribute name="supportslangversion">true</attribute>
    </task>
    <task name="vbc">
      <attribute name="supportsdocgeneration">true</attribute>
      <attribute name="supportsnostdlib">true</attribute>
      <attribute name="supportsnowarnlist">true</attribute>
      <attribute name="supportskeycontainer">true</attribute>
      <attribute name="supportskeyfile">true</attribute>
      <attribute name="supportsdelaysign">true</attribute>
      <attribute name="supportsplatform">true</attribute>
      <attribute name="supportswarnaserrorlist">true</attribute>
    </task>
    <task name="jsc">
      <attribute name="supportsplatform">true</attribute>
    </task>
    <task name="vjc">
      <attribute name="supportsnowarnlist">true</attribute>
      <attribute name="supportskeycontainer">true</attribute>
      <attribute name="supportskeyfile">true</attribute>
      <attribute name="supportsdelaysign">true</attribute>
    </task>
    <task name="resgen">
      <attribute name="supportsassemblyreferences">true</attribute>
      <attribute name="supportsexternalfilereferences">true</attribute>
    </task>
    <task name="delay-sign">
      <attribute name="exename">sn</attribute>
    </task>
    <task name="license">
      <attribute name="exename">lc</attribute>
      <attribute name="supportsassemblyreferences">true</attribute>
    </task>
  </tasks>
</framework>

…and you can then set NAnt to use it using

<property name="nant.settings.currentframework" value="net-4.0" />