Targeting .NET Core
Problem
Since DNX was announced, library authors have been inundated with requests to support .NET Core and the CoreCLR. Up until now, the only real option was to use the DNX-based project.json
build system with the Visual Studio xproj
projects. Adding these project types into an existing project that already supports a wide-range of platform targets can be challenging. There are a few issues with the current approach:
– Not all project types can be built with project.json
– It’s been a moving target as DNX is rightfully still in beta.
– Without proper guidance, authors have been targeting dnxcore50
in their packages intended for .NET Core instead of dotnet
– To be fair, dotnet
is a recent update that has been little publicized
Starting today though, there’s a better way. Just make sure to install the Windows developer tooling as it includes this new functionality.
Terminology
If we go back to the .NET Core presentation back in November, you may remember this diagram:
In terms of terminology, .NET Core should be your target; CoreCLR is just a runtime. Referring to the diagram, the dnxcore50
Target Framework Moniker refers to the box in the upper-right — it’s the ASPNet 5 app model. It is BCL + DNX specific libraries. Similarly, uap10.0
is the Windows Universal app model, BCL + Windows Runtime.
Many (most?) libraries do not actually need the DNX or WinRT dependencies. All they really need are the BCL libraries. What then is the target there? The answer is dotnet
. By using dotnet
, you instead specify your dependencies in your nuget package and your package will then run on any supported runtime, including CoreCLR, .NET Native and .NET 4.6 (assuming you’re using the newest BCL packages.)
Existing Libraries
What has been lost in the commotion around DNX, CoreCLR and .NET Core is the fact that “Profile 259″+ Portable Class Libraries, class libraries that target a minimum of .NET 4.5, Windows 8 and Windows Phone 8, can run on CoreCLR as-is. You do not need to create a new project or target newer contract/BCL references. All you need is to put your existing library into \lib\dotnet
in your NuGet package in addition to the \lib\portable-*
directory it is now and list your dependencies in the package.
The only time you might need a new project is if you have platform-specific code. In that case, the new UWP tools for Windows 10 has a better option: “Modern PCLs”. Once you install the UWP tools, create a new Class Library (Portable)
in your solution and make sure only .NET 4.6, Windows Universal 10 and ASP Net 5 is checked. When you do that, you’ll get a modern PCL that uses project.json
and pulls in the newest .NET Core packages as references. You can then use linked files, shared projects and your existing techniques to build a class library that targets .NET Core. Then, put that in your \lib\dotnet
directory and create the dependencies element for it. No magic needed. Using this technique, I was able to adapt several OSS libraries to support .NET Core in very little time.
NuGet Dependencies – the heart of dotnet
As I described in my previous post, the key to making dotnet
work is specifying all of your dependencies. This can be a tedious and error-prone process. I’ve built a tool, NuGet.ReferenceGenerator that automates creation of the dependency element for the majority of cases. The tool works with either existing compatible PCL projects and the new “modern PCL” projects.
Just add the NuSpec.ReferenceGenerator
NuGet to your package and build. I won’t go over all of the docs, but you can find those on the project site.
At build time, the tool will read the references your assembly requires, determine the source NuGet package and version, and create the <dependencies>
element in the NuSpec.
Call To Action
- If you maintain a library, review any areas where you are currently targeting
dnxcore50
and update your NuGet package to put those bits indotnet
. If you are not using anyMicrosoft.Dnx
references, and the majority of libraries do not, then there’s no reason to targetdnxcore50
whendotnet
reaches a far broader set of targets.- Bonus by using the “Modern PCL” projects and/or reusing your existing PCL, your dependencies will be the stable versions, not pre-release. That means your package can be stable too and not wait until Q1 2016!.
- If you currently have a library that’s a “System.Runtime”-based PCL, one that’s at least
portable-win8+net45+wp8
, then simply add a copy of the binary to your NuGet package in thedotnet
directory. Adding it to\lib\dotnet
and leaving a copy inlib\portable-win8+net45+wp8
allows it to work with .NET Core and the existing NuGet v2 clients. - Ensure your NuGet package lists all of its dependencies in a
<dependencies targetFramework="dotnet">
element. Use the stable package versions, not the DNX pre-release versions. If you don’t want to create and maintain this by hand, use my ReferenceGenerator. - Last, but most importantly, make sure your
nuget.exe
version is up-to-date by runningnuget update -self
. Version 2.8.6 or later is required to properly packagedotnet
.
so if I maintain a library like FluentNHibernate – what should I do? Do I need to make a separate version for dnx?
To a large degree you’re going to be limited by NHibernate. They’d need to support .NET Core/dotnet first as FluentNHibernate depends on it.
Once they do, given that you don’t already have a PCL, you would create a new csproj PCL project using VS 2015 with the UWP tools installed. Select .NET 4.6, Windows Universal 10 and ASPNet 5 as the targets. The resulting PCL project will reference the new .NET Core packages. You can use shared projects or linked files to include all of your existing source files.
Once you do that, you just need to put the library in \lib\dotnet and add the nuspec dependencies to the BCL packages. Adding a reference to my tool, NuSpec.ReferenceGenerator, to your PCL project will do that for you automatically.
This post was extremely helpful, thank you. I’m a library author who admittedly has not done a great job lately of keeping up with DNX and the latest platform targeting techniques with NuGet. I now have users asking for .NET Core support so I’m scrambling to catch up.
My projects are Flurl and Flurl.Http, and they target multiple platforms via “old-school” PCL. The latter also targets .NET 4.5 specifically because the PCL version carries extra “baggage” with it that I thought might turn off those who don’t need it. The solution uses Shared Projects to avoid code duplication.
I recently received a pull request for DNX support that I’m trying to make full sense out of. It adds a new xproj-based project (2 actually – one for Flurl and one for Flurl.Http) and a new target: dotnet5.4. Based on what I’ve read here, it seems to me we could get away with 2 targets total: dotnet and portable-*. Further, I’m trying to determine if we can replace my existing projects with the xproj versions rather than add new ones.
Based on your blog and you contributions to Json.NET, I’m fairly convinced you’re the planet’s foremost expert on this topic. 🙂 I would be more than grateful if you would be willing to take a 30-second peek at this pull request and comment on any suggestions you may have:
https://github.com/tmenier/Flurl/pull/69/files
And if you’re inclined to comment (https://github.com/tmenier/Flurl/pull/69) that would be more than welcomed as well.
Not trying to be lazy, I do need to get a better grasp of this stuff myself, but any expert guidance would greatly appreciated!
How does this compare to doing “dotnet pack” now?