Thursday, 14 August 2014

Using TeamCity to generate NuGet packages that reference other NuGet packages containing binaries for specific .Net versions

The Problem

I have been using the TeamCity continuous integration server to generate and publish NuGet packages automatically. The approach I have taken is based on that proposed by David Peden in this StackOverflow thread (see Option #1). Much appreciated, David.

This works pretty well until you try to generate a NuGet package that has dependencies on other NuGet packages, and in particular if a referenced package has different .Net builds in its lib folder. This problem can be illustrated in Visual Studio. If you change the .Net version of a project that has a NuGet reference to a package that contains specific .Net builds you might see an error.

 

image 

This is because the reference was created when the NuGet package was added to the project and has a path to the appropriate binaries in the NuGet package. Looking in the .csproj file for the references illustrates this further:

 

<ItemGroup>
    <Reference Include="Andy.French.Logging">
        <HintPath>..\packages\Andy.French.Logging.1.0.9\lib\net451\Andy.French.Logging.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
</ItemGroup>

 

Notice the logging framework reference path is to the net451 folder and therefore binaries built for that .Net version.

The same thing happened when I tried to generate the NuGet packages using David Peden’s approach without modification because it runs separate build steps for the different .Net versions. As a result in some cases the NuGet references were wrong for the step in question.

 

The Solution

For the time being I have come up with a somewhat hacky solution. It works for now but I am concerned it may not prove particularly maintainable. Time will tell.

The solution involves manually editing the .csproj file to include conditional references, something like this:

<ItemGroup>
    <Reference Condition="'$(TargetFrameworkVersion)' == 'v4.5.1'" Include="Andy.French.Logging, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
        <SpecificVersion>False</SpecificVersion>
        <HintPath>..\packages\Andy.French.Logging.1.0.9\lib\net451\Andy.French.Logging.dll</HintPath>
    </Reference>
    <Reference Condition="'$(TargetFrameworkVersion)' == 'v4.5'" Include="Andy.French.Logging, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
        <SpecificVersion>False</SpecificVersion>
        <HintPath>..\packages\Andy.French.Logging.1.0.9\lib\net45\Andy.French.Logging.dll</HintPath>
    </Reference>
    <Reference Condition="'$(TargetFrameworkVersion)' == 'v4.0'" Include="Andy.French.Logging, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
    <SpecificVersion>False</SpecificVersion>
        <HintPath>..\packages\Andy.French.Logging.1.0.9\lib\net40\Andy.French.Logging.dll</HintPath>
    </Reference>
    <Reference Include="log4net, Version=1.2.13.0, Culture=neutral, PublicKeyToken=669e0ddf0bb1aa2a, processorArchitecture=MSIL">
        <SpecificVersion>False</SpecificVersion>
        <HintPath>..\packages\log4net.2.0.3\lib\net40-full\log4net.dll</HintPath>
    </Reference>
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
</ItemGroup>

When TeamCity runs the build for each target framework the appropriate reference will be used. For more complex sets of references the following approach can be used:

<Choose>
    <When Condition="'$(TargetFrameworkVersion)' == 'v4.5.1'">
        <ItemGroup>
            <Reference Include="Andy.French.Domain.Driven.Design, Version=1.0.6.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Domain.Driven.Design.1.0.6\lib\net451\Andy.French.Domain.Driven.Design.dll</HintPath>
            </Reference>
            <Reference Include="Andy.French.Repository, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Repository.1.0.6\lib\net451\Andy.French.Repository.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework.SqlServer">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.SqlServer.dll</HintPath>
            </Reference>
        </ItemGroup>
    </When>
    <When Condition="'$(TargetFrameworkVersion)' == 'v4.5'">
        <ItemGroup>
            <Reference Include="Andy.French.Domain.Driven.Design, Version=1.0.6.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Domain.Driven.Design.1.0.6\lib\net45\Andy.French.Domain.Driven.Design.dll</HintPath>
            </Reference>
            <Reference Include="Andy.French.Repository, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Repository.1.0.6\lib\net45\Andy.French.Repository.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework.SqlServer">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net45\EntityFramework.SqlServer.dll</HintPath>
            </Reference>
        </ItemGroup>
    </When>
    <When Condition="'$(TargetFrameworkVersion)' == 'v4.0'">
        <ItemGroup>
            <Reference Include="Andy.French.Domain.Driven.Design, Version=1.0.6.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Domain.Driven.Design.1.0.6\lib\net40\Andy.French.Domain.Driven.Design.dll</HintPath>
            </Reference>
            <Reference Include="Andy.French.Repository, Version=1.0.1.0, Culture=neutral, processorArchitecture=MSIL">
              <SpecificVersion>False</SpecificVersion>
              <HintPath>..\packages\Andy.French.Repository.1.0.6\lib\net40\Andy.French.Repository.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net40\EntityFramework.dll</HintPath>
            </Reference>
            <Reference Include="EntityFramework.SqlServer">
              <HintPath>..\packages\EntityFramework.6.1.1\lib\net40\EntityFramework.SqlServer.dll</HintPath>
            </Reference>
        </ItemGroup>
    </When>
</Choose>
<ItemGroup>
    <Reference Include="System" />
    <Reference Include="System.ComponentModel.DataAnnotations" />
    <Reference Include="System.Core" />
    <Reference Include="System.Xml.Linq" />
    <Reference Include="System.Data.DataSetExtensions" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Xml" />
</ItemGroup>

 

It’s not perfect but it keeps me moving on for now.

Thursday, 14 August 2014

Saturday, 9 August 2014

ReSharper keyboard shortcuts stopped working

This post applies to ReSharper 8.2 running in Visual Studio Professional 2013 (12.0.30501.00 Update 2).

I’ve been installing a few Visual Studio extensions and ReSharper plugins recently and when I started Visual Studio this morning I found that the ReSharper keyboard shortcuts had stopped working. No problem though, it turned out to be an easy fix.

If you get this problem simply follow these steps:

  1. Go to ReSharper > Options.
  2. Locate Keyboard and Menus under the Environment section.
  3. I choose the Visual Studio keyboard scheme but you choose what’s appropriate to you.
  4. Click the Apply Scheme button.

That should be it. You may be prompted by ReSharper the first time you use certain ReSharper shortcuts to confirm what you want to do but this process has reset the ReSharper keyboard scheme.

 

image