I was pulled into the world of .NET core/.NET standard with my work on the FluentFTP library. I needed to port it to .NET core so I decided to learn the technology. After all it was just a cut-down version of the .NET framework, how hard could it be? It turned out to be a tough experience and after finding no tutorials on the subject I decided to write my own. I don’t claim this the best method, however its a method that worked for me.
My library was built on the .NET Framework 4.0, and was an ideal candidate for porting because it does not use WindowsForms or any UI controls, which are currently unavailable on .NET core. You can only use core data types, collections, file I/O, XML, images, timers, processes, etc. Pretty much everything you need to built a functional console app or backend library.
You might know that .NET core is the open-source version of .NET framework and is built by Microsoft. It splits the entire .NET framework into “packages” which are hosted on Nuget. Each package pulls in the dependency packages required, keeping your library free from bloat.
Porting this relatively simple library was a difficult experience, and I decided to write my experiences and tips to help developers port their technology to .NET core and thereby contribute to the platform. You can browse the currently available .NET core libraries at the Awesome .NET Core project. If you have ported a great library to .NET core don’t forget to add it into the list by forking and submitting a PR!
This article is written like a tutorial and aims to help you with your own project, however I will be using screenshots and example files from the FluentFTP project since it provides practical examples for each step.
A quick review of .NET core and the advantages it offers compared to .NET framework.
- Its cross platform, so your app will run the same on Windows, Unix, Mac, etc. I have still to see legit uses for this, because if you have a server-side application you could just run it on Windows Server with the full .NET framework. However with .NET core you can now run it on Unix, which is a free OS.
- Its modular, as in the entire .NET framework is cut into small pieces and uploaded onto Nuget. So your app must specify its dependency packages, which in turn have their own dependencies. When you install your app from nuget, it downloads only the packages it needs, nothing more.
- Its online-only, meaning every system that you app runs on will require an internet connection. You have to manually specify packages that you need, unlike typical .NET workflow where you just add a reference, import the class and use it.
- You will need Visual Studio 2017 Community Edition or better to compile the .NET core version of your library. Get it for free at Visual Studio Downloads. It must be installed on an internet connected PC and is free forever once you sign in. You can use VS 2015, but its outdated and not recommended.
- You will also need Visual Studio 2012 or 2015, for compiling the .NET Framework versions of your library. You can install both 2012 and 2017 on a single machine as I have done.
- You will need Windows 7 or higher since all of the steps are designed for building on a Windows machine, using batch files and the like. I still use Windows 7 so I recommend using that. Building on Unix is out of the scope of this article since it requires VS Code, which is a completely different open-source IDE from Microsoft with completely different project configuration (which is the hardest part).
- You will need NuGet Package Explorer to view your final nuget package (.nupkg) and ensure all the metadata and libraries have been pulled in successfully. If you are not publishing for nuget, skip this.
In this tutorial we aim to use a single codebase for the .NET framework and .NET core versions of our library, using
#if directives to conditionally compile code for a specific platform. Since this is hard to accomplish within a single VS 2017 project, I resorted to using two projects, a VS 2012 project for .NET Fx and a VS 2015 project for .NET core. Both projects refer to the same codebase, allowing shared code and shared fixes. No need to maintain two codebases and manually sync code over.
We will also aim to target multiple .NET framework versions with the same two projects and single codebase. You can always add more versions later using the same method.
|.NET Core 5.0||dnxcore50||FluentFTP_Core_VS2017.sln|
|.NET Standard 1.6||netstandard1.6||FluentFTP_Core_VS2017.sln|
We will finally create a NuGet package to publish our library on NuGet and I will cover required steps for creating a valid .nuspec, building your nuget package and even publishing your library on NuGet. Your library will install using NuGet on VS 2010, VS 2012 and VS 2017. This is the resultant .nupkg when viewed in the excellent NuGet Package Explorer.
The difference between .NET core and .NET standard is thus:
- .NET standard is a “standardized” set of .NET classes available on all .NET platforms, whether its .NET framework, .NET core, Xamarin, PCL or whatever. So called “one library to rule them all” (see image below by Microsoft). In regard to versions, .NET Standard 1.4 was sufficient for our library, however we required the
SslStreamclass for our implementation of FTPS which is why we had to go with .NET Standard 1.6.
- .NET core is just one implementation of .NET standard and contains a few more features, however all that is outside the scope of this article. Published into nuget with the “dnxcore50” moniker.
To quote Jon Skeet:
.NET Core is an implementation of .NET Standard. It’s available on multiple operating systems, but that’s not the same thing – there are other implementations of .NET Standard as well.
So if you create a .NET Core library, it will have access to things that are implemented in .NET Core, but aren’t part of .NET Standard, and your library won’t be compatible with other implementations of .NET Standard, such as Xamarin, Tizen, full .NET desktop framework etc.
In short: to achieve maximum portability, make your library target .NET Standard.
If you wish to download the completed project you can grab the source code ZIP of FluentFTP 17.4.2 (latest as of 5th June 2017) or you can download/fork the source from the github repository. Both of them include:
- Solution & project files for VS 2012 and VS 2017:
- For VS 2012 & .NET framework :
- For VS 2017 & .NET core :
- For VS 2012 & .NET framework :
- The shared codebase (
- Batch files to update .NET core packages (
restore.bat) and build the nuget package (
- Nuspec file for building nuget package (
- Key file for strong-name signing (
- Pre-built binaries to show the folder structure (
- Pre-built nupkg file to show the final nuget package (
CREATING PROJECTS FOR VS 2012 AND VS 2017
Use any method to create C# Library projects for VS 2012 and VS 2017. You may create new projects or use the ones I provided as a template.
Next, add build configurations for multiple .NET framework versions; open up the VS 2012 project and add build configurations using the UI, or modify the file by hand (as I did). The relevant files and changes required are listed in detail below:
Here we have 3 sets of build configurations:
Debug / Release– Builds .NET 4.5 version
DebugNET4 / ReleaseNET4– Builds .NET 4.0 version
DebugNET2 / ReleaseNET2– Builds .NET 2.0 version
Here we have the same build configurations as above, with one
PropertyGroup per configuration. The relevant options are:
DefineConstants– Add conditional compilation constants as per need (eg
NETFX, NET2, NETFX45)
OutputPath– Follow the standard naming system specified by nuget (
net20for .NET 2 and so on)
TargetFrameworkVersion– The version of .NET you need
No changes are required to the solution file for VS 2017, however you must open up the .csproj in Notepad++ and ensure that certain key variables have been filled:
TargetFrameworks– Very important. Here I used
dnxcore50(.NET Core 5.0) because I wanted to target both frameworks. If you need .NET Standard 1.4 you will need to add it here, or if you wish to target only .NET Standard then remove the
DefineConstants– Add the CORE constant for conditional compilation of the shared codebase.
NetStandardImplicitPackageVersion– We use .NET Standard 1.6.
TargetFrameworkIdentifier– We use .NET Standard.
TargetFrameworkVersion– We use .NET Standard 1.6.
AssemblyOriginatorKeyFile– If you need strong name signing, specify your key file here.
PackageReference(s)– Very important. Which assemblies in the .NET core framework you need for your library.
- Start with
System.IOand add more as you need later.
- To know which references to add, refer to the “Referencing .NET classes” section.
- To know which version number to fill in, visit the nuget page for the assembly and use the latest version offered.
- Some assemblies are implicitly included, such as
System.Collections. However you will still need to list these in the .nuspec file.
- Start with
CREATING THE NUSPEC FILE
If you are not publishing for nuget, skip this. I recommend creating your .nuspec file by hand in Notepad++, using mine as a template. Key parameters are shown and detailed below:
contains any extra .NET framework references you need. You need one entry per .NET framework version you are publishing for, so in this case
is a newer tag honored by newer nuget versions, used to specify every single .NET core dependency, even the obvious ones like
- Everytime you add a .NET core dependency, you must add it here as well (see the “Referencing .NET classes” section).
- Although I placed
entries even for the .NET framework versions, you are not required to fill anything in them, but add them in so newer nuget versions don’t break.
Once you’ve got all the projects setup, its time to begin compiling and porting your library to .NET core. Fire up VS 2017 and open your .NET core solution. If you are using my template, the file you need is
FluentFTP_Core_VS2017.sln. Hit compile. If using your library, you should see hundreds of errors. Don’t worry! Most are trivially solved.
- Cannot find type X – You need to find which .NET core assembly the required class is inside, and add a reference into your .NET Core .csproj and your .nuspec (if publishing for nuget). Remember to run
restore.batevery time you add a reference or it the compile errors will not go away! For more info, see the “Referencing .NET classes” section.
- Method X is not available in type X – It might have been renamed or removed. To check, simply open the .NET Core API reference for the class in question and manually review the members it has.
Some key points:
- Strings are available but random utility functions are missing, such as
string.IsNullOrWhitespace, so you will have to provide your own implementation for such functions.
- Reflection is not available in .NET core, so you will have to hard-code sections that use reflection.
- Tasks and Threading and async/await are available, but you will have to reference
- Async is supported (see above point) but the older
IAsyncResultbased async is not supported. You will have to disable those section using
#iftags or upgrade to
- Crypto is available but many classes are renamed and refactored, for eg.
new SHA1CryptoServiceProvider()is now
- Sockets are available but you need
System.Net.Securityif you want
socket.Dispose()so instead of two methods (Close and Dispose) you just have Dispose.
- StackTrace is available but you need the extra System.Diagnostics.StackTrace, so if its not essential you may want to remove from your code rather than add an additional dependency.
#if CORE to mark alternative code that .NET core needs. A common pattern is:
#if CORE socket.Close(); #else socket.Dispose(); #endif
And to “disable” code from compiling into your .NET core version:
#if !CORE #endif
If you are compiling for .NET 2.0 as well, then you will need to guard your imports with:
#if (CORE || NETFX) using System.Threading; #endif #if (CORE || NETFX45) using System.Threading.Tasks; #endif
Many classes and methods are renamed or deleted in order to clean-up or modernize the API. Even the classes that have not been renamed need to be referenced in a specific way for the .NET core compiler to include them in your project. So when you want to use a .NET class, this is the process:
- Find the class on the online .NET core site (eg other tools) or using any of these
- On the header you will find
Assemblies: System.IO.FileSystem.dll– this tells you the nuget package you need for that class. (see the image below)
- Add that nuget package into your .NET core “.csproj” and your “.nuspec” file. (see the images below)
- Run the “dotnet restore” command to download the new assembly. (run
- Add an import and use the class in your code.
- Hopefully by now it compiles, if not, try using the latest version of .NET standard (currently 1.6).
The .NET core docs
Adding a reference into your .NET core .csproj
Adding a reference into your .nuspec
Creating your NuGet package
Once you’ve got your libraries compiling and building, you probably want to release them to the world on NuGet. In case you haven’t caught up, NuGet is a cool way to instantly and automatically pull in libraries (and all their dependencies) into a .NET project. Its bought by Microsoft and officially supported in Visual Studio.
To test creating a nuget package, run the
build_nuget.bat. Any nuget related errors will show up in the console window. Fix them. Once everything is fine you should see “Successfully created package XYZ” in the console.
- Missing parameters. The Nuspec format keeps changing, and if you have missing parameters you will be informed by the nuget.exe when you try to build for it. Use Google to find default values for new paramaters.
- Incorrect assembly references. Nuget will sometimes warn you if assembly refs specified in the
don’t exist on Nuget. Open the nuget website (or one of these tools) and search for the exact name of the .NET core assembly you need.
Open up your nuget directory (located at
FluentFTP\nuget in my template) and double-click the newly created .nupkg file to open up your NuGet package in the NuGet Package Explorer.
- Incorrect metadata. Remember that your nuspec must be valid XML, so you cannot use in your metadata strings.
- No assemblies are shown on the file tree (right side). Check your
section in the .nuspec. To include all files in a certain directory, use this:
If all went well, you should see something like this:
I’m sure there are more automated ways to achieve this, but here are steps I use every time I need to push an update of the library to NuGet.
Compile your library:
- Increment version number in
- Increment version number in
- Increment version number in
- Open VS 2012 – Compile “NET Release”, “Release_NET2”, “Release_NET4”
- Open VS 2017 – Compile “Release”
Build and publish the nuget package:
- Run the ‘
build_nuget.bat‘ script to create a .nupkg
- Visit this link at nuget to upload a package
- Login to your nuget account (create one if you don’t have one)
- Browse to your nuget package (.nupkg)
- Add “release notes” (limited markdown is supported such as bulleted lists)
- Upload the package
- You will get an email when you package has been “published”
While the .NET Core ecosystem is being actively improved by Microsoft, it still needs a lot of work especially on the documentation side before it becomes practical to work with. Porting a simple library took over a week of hard work, and although my tutorial should help with project configuration and compiling, there is still much effort required on the part of the developer during the porting and testing phases. There should have been a “one click” application porting tool that refactors your project to work with .NET core.
The hardest part was compiling it in a nuget friendly format (discovering the
dnxcore50 moniker) and creating an valid “combined” nuspec for .NET framework and .NET core versions of the library.
I hope my tutorial helped you port your .NET library to .NET core, and if it did, add a comment below!
You will need these during your porting process to help find equivalent .NET core classes for .NET framework classes. Sometimes a class has been renamed, or sometimes its simply absent.
- .NET API Browser – Great tool by MS that helps you find classes included in .NET core / .NET standard. You can type the name of a class, such as “stream” to see which similarly named classes are included.
- Reverse Package Search – Similar to the above, only you can search for classes and members! Type the name of a class and hit ENTER (it does not have “find as you type”). It lists all similarly named classes as well as details about which .NET core / .NET standard version that class is in.
- NuGet Search – Once you know which .NET core assembly you need, search nuget to get the exact package name, and to get the latest version, which you add into your .nuspec and your .csproj.
- .NET API Portability Analyzer – A tool from MS which analyzes your project and highlights classes that are not available in .NET core. Never used it so I have no comments.
- Porting FluentFTP – The changes that were required to port FluentFTP from .NET Framework 4.0 to .NET Core 5.0 and .NET Standard 1.6. The link takes you to my github commit history for the same.
- 5 June 2017 – First version of article with Intro, Review, Requirements, Objectives, .NET core vs .NET standard, Downloads, Getting started, Referencing .NET classes, and Summary.