NuGet — Targeting both .NET Standard and the .NET Framework

Stephen Robert James Adam
6 min readJul 19, 2017

Recently we’ve been moving over to .NET Core for new projects at work. This has been fairly straightforward in most respects, though we have large number of NuGet packages which we use to share utilities, claims, API libraries and a variety of other components between our applications.

Given we want to avoid creating .NET Core versions of all of our libraries I decided to take a look at the story around creating packages which could target both our new .NET Core applications and .NET Standard libraries along with our legacy .NET Framework applications.

We’re going to learn about this by getting an old Framework package written for the .NET Framework working with a new .NET Core App.

Example source code on Github can be found here.

Compatibility Matrix

There has always been some understandable confusion regarding compatibility between new .NET Standard and legacy Framework applications. It’s understandable with the myriad of names, name changes and Microsoft getting confused and flip flopping on the issue.

The matrix below shows which versions of the .NET standard are compatible with which version of the .NET Framework.

Unfortunately it isn’t quite that simple as there a large number of Api’s which are present in the .NET Framework which are NOT present in the .NET standard. You can think of the .NET standard as an interface which defines a set of features, in our case we’ll be using 1.3. Which can (roughly) be described as a subset of 4.6.

i.e.

IFramework46 : INetStandard13 { }

Let’s get started and see what challenges we run into!

Using a .NET Framework NuGet package with a .NET Core application

For this example I created a new .NET Core console application in Visual Studio and tried adding a utility package we use internally via NuGet. This generated the following error message.

Package MyCompany.Utility 1.2.5 is not compatible with netcoreapp1.1 (.NETCoreApp,Version=v1.1). Package MyCompany.Utility 1.2.5 supports: net35 (.NETFramework,Version=v3.5)

This error comes as no surprise as by default our Core App will target the platform independent .NET Standard; our package on the other hand could be using all sorts of classes and Api’s which are only available in the .NET Framework.

The easiest way of getting up and running if you want to use legacy Framework libraries with new Core apps is to set the target Framework of your consuming Core app.

<PropertyGroup>
<OutputType>Exe</OutputType>
<! — <TargetFramework>netcoreapp1.1</TargetFramework> →
<TargetFramework>net461</TargetFramework>
</PropertyGroup>

This is not an ideal solution as our package can still only be run by clients running the .NET Framework. If you want to really harness the flexibility of .NET Core then read on for a better way!

Creating a NuGet package which targets multiple platforms

A more flexible and clean way of handling the issue is to update the package so it supports both our old .NET Framework applications and our new Core Apps.

This will require some work as many of the .NET Framework Api’s are either not available in .NET Standard or have been moved to a separate package.

To illustrate this process I have created a new .NET Standard library and have copied the Framework code from the old utility library across.

The first step is to update the csproj file and tell it we want to target both version 1.3 of the Standard Library and 4.6 of the Framework.

<Project Sdk=”Microsoft.NET.Sdk”> 
<PropertyGroup>
<TargetFrameworks>netstandard1.3;net46</TargetFrameworks>
</PropertyGroup>
</Project>

Fixing our first method

Let’s start of with this DeserializeXmlDocument method.

Prior to .NET Core the XmlSerializer lived in the System.Xml library which was automatically added to new projects. The push with Core was to break out functionality into separate packages to make applications faster and smaller and leaner.

After a quick google the I found the .NET Standard package for the System.Xml.XmlSerializer package and installed it via NuGet.

After installing that we still have a couple of issues to address. The signature for the StreamReader has changed and now only accepts a Stream rather than a path and the close method is missing from the StreamReader.

This all makes sense, the StreamReader now follows the Single Responsibility principle and no longer handles file access, the close method was superfluous as the reader implements the IDisposable interface which allows us to wrap it in a using statement.

Fixing these means we need to refactor the code a bit, but the changes are trivial. Given the System.Xml.XmlSerializer package supports both the .NET Standard 1.3 and the .NET Framework 4.61. We now have a method of serializing Xml which will work across platforms!

Conditional Compilation

The next issue I ran into was the ConfigurationHelper class. This is a set of methods which work on the ConfigurationManager manager.

The ConfgurationManager has been with us since .NET 2.0 and is tightly coupled to the standard framework Xml configuration files (web.config / app.config).

With these gone and replaced with a totally new configuration system in Core we have a problem. Our existing legacy applications still need to make use of this class but our new Core apps simply wouldn’t be able to run with this code still present.

Conditional compilation allows us to target different blocks for different frameworks. Below is the update which was required on the csproj file.

We conditionally load in dependencies setup constants we can use in the library to specify which code to compile based on the framework.

With our new conditional constants defined we get this drop down menu to select which platform to interpret the code from. Below we are looking at the code using the .NET Standard 1.3 and as such the code is grayed out as it will not be compiled for that Framework.

Here we can see the same class with the 4.6 Framework option selected.

Building the package

The tooling has also moved on from the previous method of invoking ‘nuget pack’ at the command to the new dotnet-pack command.

C:\Projects\NuGetExample\Standard.Utils>dotnet pack

This creates the nuget package in the ‘bin\Debug’ folder.

An alternative is to use msbuild. To use this method we need to open up the Visual Studio Developer Command Prompt and run the following command in the project directory.

msbuild /t:pack /p:Configuration=Release

Note: if you want to execute the msbuild command above from the standard command line then you’ll need to add the following to your Path variables.

C:\Program Files (x86)\Microsoft Visual Studio\2017\Professional\MSBuild\15.0\Bin

--

--