Frank Bursitzke
Jun 2, 2017 | Last updated: Dec 16, 2022
Expert articles | 4 min read

I have a Windows service that must be able connect to CRM systems that are version 2011 or newer. Previously, the strategy for this scenario had been to use the lowest version of dependencies, which means CRM SDK 5 (2011) and .NET Framework 4.0. This used to work with CRM 2011, 2013, 2015 and 2016 OnPremises, but there was a problem with CRM 2016 Online due to changes in the login mechanism. Connecting to CRM 2016 Online was only possible using the current SDK version 8.0.

Two obvious solutions to this problem presented themselves:

  1. Always compile using the latest CRM SDK version.
  2. Maintain a second branch in version control.

However, both solutions have major disadvantages:

  1. This would have changed the dependency of .NET Framework to 4.5.2, which was not an option.
  2. Another branch means additional maintenance effort.

So, I wanted to find a third, more elegant solution using msbuild-magic.

Even though this example uses the Microsoft CRM SDK, it can be applied to any third-party library. Additionally, by using a conditional compilation symbol, the behaviour of your application may be adopted to different versions of your api (e.g. use new features, omit deprecated functions).

Creating a solution using msbuild

Usually, if you run into this problem, you already have a rather complex solution in place. So, I will keep it brief.

I use a simple console application based on .NET 4.0 in this example. Additionally, I will create a _lib directory containing sub-directories for each version of my third-party assemblies. (A solution using nuget would be preferable. I, however, have not tried this yet.)

My example solution looks like this:

Structure-of-the-example solution
Structure of the example solution

Preparation: creating a new msbuild configuration

I now create a new build configuration Release_2016, copying settings from the existing Release configuration.

Subsequently, I define a conditional compilation symbol CRM2016 (any name will do) in the project settings on the Builds tab. This is an optional step and may be used for conditional compile. However, it’s not necessary for what we want to achieve in this example solution.

build-configuration-symbol
Build-configuration “Release_2016” with conditional compilation symbol

The next steps require editing the project file itself. Therefore, the project has to be unloaded in the Solution Explorer (right click → Unload project) and afterwards opened in the XML editor (right click → Edit ConsoleApplication_Example.csproj).

Conditional compilation: .NET Framework Version

To overwrite the .NET Framework 4.0 version, which is used by default for the configuration „Release_2016“, a TargetFrameworkVersion element must be added to the approriate PropertyGroup and set to Version 4.5.2:


 <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release_2016|AnyCPU'">
  <OutputPath>bin\Release_2016\</OutputPath>
  <DefineConstants>TRACE;CRM2016</DefineConstants>
  <Optimize>true</Optimize>
  <DebugType>pdbonly</DebugType>
  <PlatformTarget>AnyCPU</PlatformTarget>
  <ErrorReport>prompt</ErrorReport>
  <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
  <TargetFrameworkVersion>v4.5.2</TargetFrameworkVersion>
 </PropertyGroup>

Now, when choosing the build configuration Release_2016, the application is compiled against .NET 4.5.2. For all other configurations version 4.0 is used.

Conditional compilation: Third Party Assemblies

This is where things get a little tricky. First, the references to the third-party assemblies must be removed from the default ItemGroup element. Therefore, the approriate Reference elements must be deleted.

Subsequently, an MSBuild Choose element is used to add the references to the specific version in a new ItemGroup. When choosing configuration „Release_2016“, we want the assemblies from _lib\CRM 2016 to be referenced. In all other cases, those from _lib\CRM 2011 should be referenced.

This looks like the following:

<figure>
<pre><code tabindex="0" spellcheck="false"><Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!—snip… -->
   <Choose>
     <When Condition="'$(Configuration)' == 'Release_2016'">
        <ItemGroup>
           <Reference Include="microsoft.crm.sdk.proxy">
              <HintPath>..\..\_lib\CRM 2016\microsoft.crm.sdk.proxy.dll</HintPath>
           </Reference>
           <Reference Include="microsoft.xrm.sdk">
              <HintPath>..\..\_lib\CRM 2016\microsoft.xrm.sdk.dll</HintPath>
           </Reference>
       </ItemGroup>
     </When>
     <Otherwise>
        <ItemGroup>
           <Reference Include="microsoft.crm.sdk.proxy">
              <HintPath>..\..\_lib\CRM 2011\microsoft.crm.sdk.proxy.dll</HintPath>
           </Reference>
           <Reference Include="microsoft.xrm.sdk">
              <HintPath>..\..\_lib\CRM 2011\microsoft.xrm.sdk.dll</HintPath>
           </Reference>
       </ItemGroup>
    </Otherwise>
 </Choose>
</Project>
</code></pre>
</figure>

Result

After reloading the project in the Solution Explorer, we can conditionally compile against either .NET 4.0 and CRM 2011 SDK or .NET 4.5.2 and CRM 2016 SDK, depending on the build configuration.

Since only standard MSBuild mechanisms are used, this solution works both when compiling in Visual Studio as well as in continuous integration Builds, for example TFS.

Answering