Introduction
When using specific third party assemblies it is necessary to distinguish between platforms. Best example are e.g. log4net, sqlite, … which come in both x86 and x64 but not in the general Any Cpu flavor. A typical development scenario could be that an ASP.NET application is developed using the IIS Express and deployed on a 64bit IIS in production.
A different development scenario could be that a Silverlight application uses Design Time data in order to create a richer design time experience for the developer. In this situation a best practice is to put the classes containing the design time data into a separate assembly that is only referenced during development and is not deployed in production. Here one needs to distinguish between solution configuration Debug and Release.
In order to support these scenarios the project files allow for conditional references.
Unfortunately Visual Studio does not expose the conditional references feature in its UI. Therefore one has to edit the corresponding .csproj file by hand. This can be simply done using Visual Studio using the following steps:
- Right click the project one wants to edit
- Select "Unload Project" from the popup menu
- Right click the unloaded project
- Select "Edit
"
Basic project file layout (Visual Studio 2010)
A project file (.csproj) is an XML file containing an XML header and a root element Project. The Project can contain multiple elements of different types, most notably the PropertyGroup, ItemGroup, Import and Target elements. In this post I will not describe the specific use of each of the elements, but will concentrate on conditional assemblies. Documentation for these elements can be found on msdn.
Assembly or project references are found in an ItemGroup using either Reference or ProjectReference elements.
A typical .csproj producing either an x86 or x64 executable looks like this:
- <?xml version="1.0" encoding="utf-8"?>
- <Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
- <PropertyGroup>
- <Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
- <Platform Condition=" '$(Platform)' == '' ">x86</Platform>
- <ProductVersion>8.0.30703</ProductVersion>
- <SchemaVersion>2.0</SchemaVersion>
- <ProjectGuid>{5793FBD7-6122-484B-AD86-AA211162D0EB}</ProjectGuid>
- <OutputType>Exe</OutputType>
- <AppDesignerFolder>Properties</AppDesignerFolder>
- <RootNamespace>Com.Hertkorn.DotNet.ConditionalAssemblyReferences</RootNamespace>
- <AssemblyName>Com.Hertkorn.DotNet.ConditionalAssemblyReferences</AssemblyName>
- <TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
- <TargetFrameworkProfile>
- </TargetFrameworkProfile>
- <FileAlignment>512</FileAlignment>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|x86' ">
- <PlatformTarget>x86</PlatformTarget>
- <DebugSymbols>true</DebugSymbols>
- <DebugType>full</DebugType>
- <Optimize>false</Optimize>
- <OutputPath>bin\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
- <PlatformTarget>x86</PlatformTarget>
- <DebugType>pdbonly</DebugType>
- <Optimize>true</Optimize>
- <OutputPath>bin\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <ErrorReport>prompt</ErrorReport>
- <WarningLevel>4</WarningLevel>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
- <DebugSymbols>true</DebugSymbols>
- <OutputPath>bin\x64\Debug\</OutputPath>
- <DefineConstants>DEBUG;TRACE</DefineConstants>
- <DebugType>full</DebugType>
- <PlatformTarget>x64</PlatformTarget>
- <CodeAnalysisLogFile>bin\Debug\Com.Hertkorn.DotNet.ConditionalAssemblyReferences.exe.CodeAnalysisLog.xml</CodeAnalysisLogFile>
- <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
- <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
- <ErrorReport>prompt</ErrorReport>
- <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
- <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
- <CodeAnalysisIgnoreBuiltInRuleSets>true</CodeAnalysisIgnoreBuiltInRuleSets>
- <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
- <CodeAnalysisIgnoreBuiltInRules>true</CodeAnalysisIgnoreBuiltInRules>
- </PropertyGroup>
- <PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
- <OutputPath>bin\x64\Release\</OutputPath>
- <DefineConstants>TRACE</DefineConstants>
- <Optimize>true</Optimize>
- <DebugType>pdbonly</DebugType>
- <PlatformTarget>x64</PlatformTarget>
- <CodeAnalysisLogFile>bin\Release\Com.Hertkorn.DotNet.ConditionalAssemblyReferences.exe.CodeAnalysisLog.xml</CodeAnalysisLogFile>
- <CodeAnalysisUseTypeNameInSuppression>true</CodeAnalysisUseTypeNameInSuppression>
- <CodeAnalysisModuleSuppressionsFile>GlobalSuppressions.cs</CodeAnalysisModuleSuppressionsFile>
- <ErrorReport>prompt</ErrorReport>
- <CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
- <CodeAnalysisRuleSetDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\\Rule Sets</CodeAnalysisRuleSetDirectories>
- <CodeAnalysisIgnoreBuiltInRuleSets>false</CodeAnalysisIgnoreBuiltInRuleSets>
- <CodeAnalysisRuleDirectories>;C:\Program Files (x86)\Microsoft Visual Studio 10.0\Team Tools\Static Analysis Tools\FxCop\\Rules</CodeAnalysisRuleDirectories>
- <CodeAnalysisIgnoreBuiltInRules>false</CodeAnalysisIgnoreBuiltInRules>
- </PropertyGroup>
- <ItemGroup>
- <Reference Include="CustomMarshalers" />
- <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>
- <ItemGroup>
- <Compile Include="Program.cs" />
- <Compile Include="Properties\AssemblyInfo.cs" />
- </ItemGroup>
- <ItemGroup>
- <None Include="app.config" />
- </ItemGroup>
- <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
- <!-- To modify your build process, add your task inside one of the targets below and uncomment it.
- Other similar extension points exist, see Microsoft.Common.targets.
- <Target Name="BeforeBuild">
- </Target>
- <Target Name="AfterBuild">
- </Target>
- -->
- </Project>
Conditionals
In the above project one can already spot the mechanism used to distinguish between e.g. configurations and platforms.
- <PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|x86' ">
An XML node annotated with the Condition attribute is only included when parsing the file if the condition holds true. This mechanism can be applied to any element in the project file's XML structure.
Using conditions for assembly references
If one uses an assembly that is specifically designed for x86 or x64 one can use the conditional attribute to include the correct dll for the specified build platform. One of the best known dll of that type is SQLite:
Without the conditional a dll reference looks like this:
- <Reference Include="System.Data.SQLite">
- <HintPath>..\libs\sqlite\System.Data.SQLite.dll</HintPath>
- </Reference>
To distinguish between the two states of 32bit and 64bit one simply doubles theReference element and applies mutually exclusive Condition attributes to both:
- <Reference Include="System.Data.SQLite" Condition="'$(PlatformTarget)' == 'x86'">
- <HintPath>..\libs\sqlite\x86\System.Data.SQLite.dll</HintPath>
- </Reference>
- <Reference Include="System.Data.SQLite" Condition="'$(PlatformTarget)' == 'x64'">
- <HintPath>..\libs\sqlite\x64\System.Data.SQLite.dll</HintPath>
- </Reference>
Note that the HintPath differs depending on the platform.
Using conditions for project references
If you have a project that should not get deployed as part of the final released product, why not use the Condition attribute to only include the project during debugging. One good example for these kinds of projects is Design-time data. Simply distinguish on the Configuration setting.
- <ProjectReference
- Include="..\Com.Hertkorn.DotNet.ConditionalAssemblyReferences.Design\Com.Hertkorn.DotNet.ConditionalAssemblyReferences.Design.csproj"
- Condition="'$(Configuration)' == 'Debug'">
- <Project>{C7EAA6CA-1AF6-4D72-929B-D7D1177868D2}</Project>
- <Name>Com.Hertkorn.DotNet.ConditionalAssemblyReferences.Design</Name>
- </ProjectReference>
Hint
Keep in mind that the Visual Studio UI does not reflect conditional references within the "References" folder of a project. Therefore all references are visible in any given configuration or platform selection. The compiler and Intellisense on the other hand are aware of conditional references, honoring the correct settings both with visual feedback and error notification during builds.
Summary
Today we looked at the possibility to customize which assemblies or projects are referenced in each individual configuration or platform selection. The customization cannot be done using the UI, but is accomplished by manually editing the project file.
