Tuesday, March 3, 2020

Developing .Net nuget package that targets multiple frameworks

Background

Every serious developer might have accumulated a set of reusable code snippets over the course of their career. Mostly those snippets help them to speed up work. With the advent of packaging techniques such as nuget, NPM it is very easy to share between projects. 'DotNet.Helpers' is such a library for sharing reusable .Net code which makes the development faster.

.Net uses the nuget package repository. There are different versions in .Net and maintaining a separate project and nuget package for each version is not practical.

.Net has a solution for the same and its called multi-targeting.

This post is to discuss some of the use cases, issues faced during the development of 'DotNet.Helpers' library. Below is the list.

What are the use cases in multi-targeting?

  • Conditional references
  • Conditional compilation
  • Conditional inclusion of files
  • Unit testing against each framework
  • Documentation against each framework
But before discussing in details lets see how the internet says about it.

Microsoft way

The first thing Microsoft ensure that the nuget package structure is independent of how we compile and build package. Basically, the nuget package is a zip file. If it has the right folder structure, and metadata it just works.

https://docs.microsoft.com/en-us/nuget/create-packages/supporting-multiple-target-frameworks

The tooling support for multi-targeting started from VS 2015 onwards. SDK-Style projects help us mention all the frameworks in a single .csproj file itself.

How other libraries did it?

Though Microsft has tooling for multi-targeting, it is difficult to exact guidance to start our own multi-targeting nuget package. Better to look at how other libraries did the same.

NewtonSoft.JSON - This is a famous library for JSON related operations. It uses the SDK-Style project and CI/CD done using AppVeyor and Azure Pipelines. But it is a little difficult to start a new project by just following it.

An easier way is documented at west-wind.com. It explains how single repo can be used to hold source code which can be used to build a nuget package having different target frameworks.

Use cases

Most of the solutions presented below are tested against VS 2019. None of the features require a paid version of VS. Just community will be enough.

Conditional references

When we develop our library, we may be referring to other assemblies but not required in certain frameworks. For example, mscorlib.dll is only needed when we target the full framework. Similarly, there are more. Those references can be made only to the full framework using the below method.

<ItemGroup Condition="Condition"> 

A sample can be found in the repo

The same which works for assemblies can be extended to nuget library references too. Here is the link from the same repo.

Conditional compilation

This is the same as what is there from the beginning. We can write code using the conditional compilation constants and the value of constants can be controlled from the SDK-Style .csproj file.

Here is the link to defining the constant based on the framework version and here is the link to using it in the source code.

Conditional inclusion of files

Another scenario is including the files based on the framework. This is useful mainly if the entire file is framework dependent than some sections inside. 

It can be done by using the same technique which can be done in the .csproj file.

<Compile Remove="<file pattern>"

A sample link is here.

Unit testing against each framework

The approach is the same as how we are developing the library. Create a unit test project with the SDK-Style template. Use conditional inclusion and compilation techniques described above. Based on the below tag in the test project file, tests will be run against all the mentioned frameworks.

<TargetFrameworks>net472;netcoreapp2.1;netcoreapp3.1</TargetFrameworks>

Here is the link to DotNet.Helpers.Tests.csproj

Documentation against each framework

This is the tricky part. To be frank there is no solution implemented for this in DotNet.Helpers library. It uses DocFX to generate documentation and generate using a full framework which is kind of superset of others.

Generated DoxFX contents, doesn't seem to be providing any way to filter APIs based on frameworks.

Support for AppVeyor for CI & CD

This is a bonus section. If we get the project set up correctly, we can easily integrate with CI/CD systems such as AppVeyor. It is free for open-source projects.

Links

http://blogs.microsoft.co.il/iblogger/2017/04/05/easily-supporting-multiple-target-frameworks-tfms-with-vs2017-and-nuget/
https://www.cyotek.com/blog/targeting-multiple-versions-of-the-net-framework-from-the-same-project

No comments: