Working with .NET Standard is a pleasure. It’s fun, it’s short, it’s amazingly readable and concise. As a developer, I would like to work only with .NET Core or .NET Standard code. However, sometimes you need to support the old .Net Framework as well. How to create a NuGet package, that can be used by both?
A real-life example
I’m an author and a developer of a simple client for an Egnyte API, which is a private cloud for documents with a ton of features. It is a package, that I created as PCL (Portable Class Library), cause it can support multiple frameworks while still programming in full .Net Framework.
However recently I was asked to write support for a .Net Standard as well so that it can be used by newer projects. I happily accepted the opportunity and started research around the topic.
It turned out, that when you need to support multiple frameworks, the preferred way is no longer PCL, but .Net Standard. So that’s what I did. I ported my package to .Net Standard and make it work. Luckily it is a very simple project, so that wasn’t a very hard task. What’s more, if you would like to support multiple frameworks, you just need to list them in your
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>netstandard2.0;net461</TargetFrameworks> </PropertyGroup> <ItemGroup> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> </ItemGroup> </Project>
Notice that I changed
TargetFrameworks and now I can provide a list of frameworks to support. More of targeting multiple frameworks you can read on this Microsoft page.
Why it doesn’t build?
It seems unbelievable easy up to this point, but the truth is that it can get more tricky. When I built my project, I got an exception:
Error CS0234: The type or namespace name ‘Http’ does not exist in the namespace ‘System.Net’ (are you missing an assembly reference?)
System.Net.Http library could not be found, because types that are in this namespace are a part .Net Standard. I didn’t need to import it. However, for .Net Framework 4.6.1 I need to import this package to make my code work. So after some digging, I figured out, I need to modify my
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>netstandard2.0;net461</TargetFrameworks> </PropertyGroup> <!--Conditionally obtain references for the .NET Framework 4.6.1 target--> <ItemGroup Condition=" '$(TargetFramework)' == 'net461' "> <Reference Include="System.Net.Http" /> </ItemGroup> <ItemGroup> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> </ItemGroup> </Project>
Luckily that was the only problem, but you might need to do more if-s and even hacking inside the code with code that would run only for a specific framework.
#if NET40 Console.WriteLine("Target framework: .NET Framework 4.0"); #elif NET45 Console.WriteLine("Target framework: .NET Framework 4.5"); #else Console.WriteLine("Target framework: .NET Standard 1.4"); #endif
Now my project tree looks like this. As you can see, now in the Dependencies I have two frameworks listed.
Creating a NuGet package
With .Net Standard there is no easier thing to do, then creating a NuGet package. You just need to run a command
dotnet pack in your main project folder.
In this case, I needed some more information for a package, like an author and description, so I modified my
.csproj file and here is my final version.
<Project Sdk="Microsoft.NET.Sdk"> <PropertyGroup> <TargetFrameworks>netstandard2.0;net461</TargetFrameworks> <PackageId>Egnyte.Api</PackageId> <Version>2.0.0-alpha1</Version> <Authors>Michal Bialecki</Authors> <Company>Egnyte Inc.</Company> <Description>Egnyte Api client for .net core</Description> <GenerateDocumentationFile>true</GenerateDocumentationFile> </PropertyGroup> <!--Conditionally obtain references for the .NET Framework 4.6.1 target--> <ItemGroup Condition=" '$(TargetFramework)' == 'net461' "> <Reference Include="System.Net.Http" /> </ItemGroup> <ItemGroup> <PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> </ItemGroup> </Project>
One thing worth noticing is
<GenerateDocumentationFile>true</GenerateDocumntationFile>, cause it generates XML documentation along with dlls. This is where IntelliSense takes it’s method descriptions from, which is very useful for developers.
Now if I open my NuGet package in the NuGet Package Explorer, I see something like this:
Now it supports both .Net Standard 2.0 and .Net Framework 4.6.1.
Let’s see how it looks like when installing this package.
Egnyte.Api package includes information about what framework it supports and that it is a
prerelease, cause I intentionally set the version to
2.0.0-alpha1. While installing the package NuGet Package Manager will decide which dll is right for you project framework and install the right one. Nice and easy, everything is done automatically!
Targetting multiple frameworks in .NET Standard is really simple, but it can get harder when using many references and libraries. However, it is worth the pain, cause you could work with .Net Standard instead of old frameworks and projects.
Creating and building a NuGet package is way simpler and you can use all the .Net Standard cool features. You also would be more up-to-date with .Net framework changes.
.Net Standard is the new PCL, a standard for building libraries for multiple frameworks and platforms. Also, NuGet supports having multiple dlls in one package and it chooses the right one for you. If it is done automatically, why not try it out?
All code mentioned here is available in egnyte-dotnet GitHub repository.
One thought on “How to create a NuGet package targeting multiple frameworks”
This post is very helpful, thank you very much, but I have a question, is there any way to copy the appsettings.json file only to the netstandard folder instead of being copied on both?