.NET core/.NET standard is the latest incarnation of the .NET platform. This tutorial introduces you to this new world, and gives you practical advice to successfully port your .NET Framework library to .NET Core.
This article is up-to-date as of June 2017, unlike many other tutorials and guides on the internet.
Anything that mentions a "project.json" is outdated with VS 2017 which requires a .csproj instead.
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 is the best method, however it's 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 build 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.
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 2017 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.
Platform Binaries Folder Solution .NET 2.0 net20 FluentFTP_NET_VS2012.sln .NET 4.0 net40 FluentFTP_NET_VS2012.sln .NET 4.5 net45 FluentFTP_NET_VS2012.sln .NET Core 5.0 dnxcore50 FluentFTP_Core_VS2017.sln .NET Standard 1.6 netstandard1.6 FluentFTP_Core_VS2017.slnWe will finally create a NuGet package to publish our library on NuGet and I will cover the 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.
.NET Core vs .NET StandardThe difference between .NET Core and .NET standard is thus:
SslStream
class for our implementation of FTPS which is why we had to go with .NET Standard 1.6.dnxcore50
" moniker.To quote Jon Skeet:
DownloadsQuote:
.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.
Included in the ZIP and the Github RepoUse 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:
FluentFTP_NET_VS2012.slnHere, we have 3 sets of build configurations:
Debug / Release
- Builds .NET 4.5 versionDebugNET4 / ReleaseNET4
- Builds .NET 4.0 versionDebugNET2 / ReleaseNET2
- Builds .NET 2.0 versionHere, 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 (e.g., NETFX, NET2, NETFX45
)OutputPath
- Follow the standard naming system specified by nuget (net20
for .NET 2 and so on)TargetFrameworkVersion
- The version of .NET you needNo 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 netstandard1.6
and 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 dnxcore50
moniker.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.
System.IO
and add more as you need later.System.Runtime
and System.Collections
. However, you will still need to list these in the .nuspec file.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:
<frameworkAssemblies>
contains any extra .NET Framework references you need. You need one entry per .NET Framework version you are publishing for, so in this case net20
, net40
and net45
.<dependencies>
is a newer tag honored by newer nuget versions, used to specify every single .NET core dependency, even the obvious ones like System.Runtime
and System.Collections
.
<group>
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, it's 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.
Useful TechniquesUse #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 // advanced code that only works on .NET framework #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; #endifCommon Errors
System.Reflection
System.Reflection.Primitives
System.Reflection.Extensions
System.Reflection.TypeExtensions
System.Reflection.Emit
and System.Reflection.Emit.ILGeneration
System.Threading.Thread
System.Threading.Tasks
System.Net.Sockets
.System.Net.Security
if you want SslStream
.socket.Close()
is now socket.Dispose()
IAsyncResult
-based async is not supported. You will have to disable those sections using #if
tags or upgrade to async/await
.System.Runtime.Serialization.Xml
and System.Runtime.Serialization.Json
)System.Drawing.Image
) but for the geometry primitives like Point
and Rect
, see System.Drawing.Primitives
. new SHA1CryptoServiceProvider()
is now SHA256.Create()
.System.Diagnostics.StackTrace
, so if it's not essential, you may want to remove from your code rather than add an additional dependency.System.Data namespace
but other features like the provider model and SQL client are available.System.AppDomain
- App DomainsSystem.Drawing.Image
- Graphics, Bitmap ImagesSystem.DirectoryServices
- LDAP, Active DirectorySystem.Transactions
- ambient transactions, distributed transactionsSystem.Xml.Xsl
- XSLTSystem.Xml.Schema
- XSDSystem.Net.Mail
- Sending EmailSystem.Runtime.Remoting
- Remoting, RPCSystem.Runtime.Serialization.Xml
- Binary SerializationSystem.IO.Ports
- Serial PortSystem.Workflow
- Windows Workflow FoundationMany 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:
dotnet restore
” command to download the new assembly (run restore.bat)import
and use the class in your code.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. It's 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.
Common errors:
<dependencies>
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.
Common errors:
<
and >
in your metadata strings.<files>
section in the .nuspec. To include all files in a certain directory, use this:<files> <file src="bin\Release\**" target="lib" /> </files>
If all went well, you should see something like this:
Publishing to NuGetI'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 LibraryWhile 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!
Thank you.
Useful LinksYou 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 it's simply absent.
stream
" to see which similarly named classes are included.RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4