MODERN F# DEVELOPMENT USING .NET SDK

An Open F# conference workshop

How to migrate an existing project to .NET Standard or .NET Core

If you have an existing project and want to add support for .NET Standard or .NET Core, there are three parts to consider

NOTE The .NET Sdk doesnt support just .NET Core/Standard, but also normal .NET Framework.

That mean you can create a console app/lib to just target net461 (.NET Framework 4.6.1). Or both, so .net core (netcoreapp2.0) and net461

Best way is:

Example of migration

If you dont have a project you want to convert, you can try with

https://github.com/fsprojects/FSharpx.Collections

The only rule are:

Convert the library, target NETStandard

cd src
mkdir FSharpx.Collections.NetStandard
dotnet new lib -lang F# -n FSharpx.Collections -o .

replace

Include="

with

Include="..\FSharpx.Collections\

Convert the test

For tests, dependes on test framework. Here is NUnit

See the avaiable templates list for dotnet new

dotnet new -i "NUnit3.DotNetNew.Template::*"

now let’s create the project.

cd tests
mkdir FSharpx.Collections.Tests.NetStandard
dotnet new nunit -lang F#

Tests can be run with dotnet test.

Let’s reuse the existing tests now.

First remove the UnitTest1.fs generated by template.

After that, copy the same test list from source project, and update paths of Compile items

The dependencies in paket.references are FsCheck and FsUnit.

The FsCheck has a prerelease version who support .NET Standard, so let’s try use that.

dotnet add package FsCheck --version 3.0.0-alpha1

and FsUnit

dotnet add package FsUnit --version 3.0.0

Now just the project reference to library:

dotnet add reference ..\..\src\FSharpx.Collections.NetStandard\FSharpx.Collections.fsproj

and let’s test it with

dotnet test

And there are errors. Usually because we upgraded version of out dependencies

TIP creating a solution now may help, because vscode can load just these projects From root of repo:

dotnet new sln -n FSharpx.Collections.NetStandard
dotnet sln FSharpx.Collections.NetStandard.sln add src\FSharpx.Collections.NetStandard\FSharpx.Collections.fsproj
dotnet sln FSharpx.Collections.NetStandard.sln add tests\FSharpx.Collections.Tests.NetStandard\FSharpx.Collections.Tests.NetStandard.fsproj

And reload workspace with

> F#: Change Workspace or Solution

Now back to the errors:

The nunit constraint NullOrEmptyStringConstraint doesnt exists anymore. Quick fix, ifdef the code based on target framework. Better is to do it based on nunit version (or update the nunit for .net too)

#if NETCOREAPP2_0
#else
let NullOrEmptyString = new NullOrEmptyStringConstraint()
#endif

also #if !NETCOREAPP2_0 can be used, if the project doesnt want to support older compilers

and fix build errors and tests until works :D

package it

Run

dotnet pack -c Release

or dotnet pack -c Release /p:Version=1.2.3 to generate a specific version

The -c Release with build it in Release configuration, if needed

add the netstandard to the package

Is possibile to make the project target both frameworks (so net461 and netstandard2.0) using TargetFrameworks (plural) And creating the package with just one dotnet pack, so pratically replacing the old way.

But if you want to just add netstandard, we can use the old way, so no changes needed for .NET Framework

This can be done using a .net cli tool: dotnet-mergenupkg

The idea is to use dotnet mergenupkg to add the .net standard part of the just built package, to the existing package

Is a .net cli tool, so can be added to any project adding a <DotnetCliToolReference, but because is a generic tool, can be moved in a shared tools project, because not really related to the project itself

Create a directory tools and change current directory to it.

Create a tools.proj. It’s just an empty proj with the .net cli tool reference

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <DotNetCliToolReference Include="dotnet-mergenupkg" Version="2.*" />
  </ItemGroup>

</Project>

and restore it with

dotnet restore

Now when the current directory is this directory can be executed with dotnet mergenupkg (no - sign), like:

dotnet mergenupkg --help

We can now use that to merge the two packages:

dotnet mergenupkg --source ../bin/FSharpx.Collections.1.17.0.nupkg --other ../src/FSharpx.Collections.NetStandard/bin/Release/FSharpx.Collections.1.0.0.nupkg --framework netstandard2.0

And the package now support .NET Standard 2.0 and is ready to be published

Results

The result of this, is in the PR fsprojects/FSharpx.Collections#80

Additional work

Use paket instead of project references, some info in paket docs