Conditional References in C# Project Files
New .csproj
format
One really nice thing about the .NET Core tooling is the new csproj file format, which is a lot less verbose.
For example, if you create a brand new C# library project, targeting .NET Standard 2.0, then here's the entirety of the .csproj
file:
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
</Project>
It doesn't even require you to specify all the C# files to be compiled - it just assumes that all of them are wanted, which cuts down on a lot of noise.
Creating a NuGet package on build
If you want to create a NuGet package for your library, there's no need for a separate .nuspec
file - you can just enable GeneratePackageOnBuild
and define any metadata. The Visual Studio project properties dialog simplifies setting this up.
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<Authors>Mark Heath</Authors>
<Version>1.0.1</Version>
<PackageId>MyNugetLibrary</PackageId>
</PropertyGroup>
Multi-Targeting
Another thing you can do, is multi-target more than one framework, which can be useful when you're creating a NuGet package intended to be used on several different versions of .NET. Here's how a project can target both .NET Standard 2.0 and .NET 4.6.2:
<PropertyGroup>
<TargetFrameworks>netstandard2.0;net462</TargetFrameworks>
</PropertyGroup>
If we build this now, we'll get a NuGet package containing separately built DLLs for both targets. By the way, I'm using the excellent NuGet Package Explorer utility here to look inside my .nupkg
file.
Conditional References
One issue you might run into is that you need to reference different assemblies or NuGet packages depending on which target you're building for.
For example, it we add the following method to our library, it will successfully compile the .NET Standard 2.0 target, but the .NET 4.6.2 target will fail because it can't find the definition of HttpUtility
.
public string JavaScriptEncode(string input)
{
return HttpUtility.JavaScriptStringEncode(input);
}
To fix this we need to add a reference to the System.Web
assembly, but it's only needed for the .NET 4.6.2 target. To do that, we simply add a conditional reference to our .csproj
file using the following syntax:
<ItemGroup Condition=" '$(TargetFramework)' == 'net462' ">
<Reference Include="System.Web" />
</ItemGroup>
Unfortunately, there doesn't seem to be any way of getting Visual Studio to auto-generate this for you, and the conditional syntax can be hard to remember, which is why I've documented it here so I can find it again in the future next time I forget it!
If it was a NuGet package you wanted to conditionally reference, that's done in the same way, just using PackageReference
instead. I'm also showing here how you can use multiple conditions, so we'll reference this whether we're targeting .NET 4.6.2 or 3.5:
<ItemGroup Condition=" '$(TargetFramework)' == 'net462' or '$(TargetFramework)' == 'net35'">
<PackageReference Include="NAudio" Version="1.8.5" />
</ItemGroup>
Conditional Compile
Sometimes you might want to exclude some C# files from being compiled for a certain target. For example, here's how in NAudio for the .NET 3.5 target, I'm referencing the System.Windows.Forms
assembly, and excluding three specific files from being compiled:
<ItemGroup Condition=" '$(TargetFramework)' == 'net35' ">
<Reference Include="System.Windows.Forms" />
<Compile Remove="Wave\WaveOutputs\WasapiOutRT.cs" />
<Compile Remove="Wave\WaveInputs\WasapiCaptureRT.cs" />
<Compile Remove="Wave\WaveOutputs\WaveFileWriterRT.cs" />
</ItemGroup>
Summary
The new .csproj
file format makes it really easy to develop NuGet packages that multi-target several frameworks, but you will likely occasionally need to use the conditional syntax to make this work.
Comments
Conditional packagereferences are currently broken. Workaround here:
Luke Schoenhttps://github.com/NuGet/Ho...
thanks for the heads up
Mark Heath