.NET Standard and .NET Core have been on my mind for a long time now - years really, but the reality is while I've been using the technology quite a bit, I've not jumped in with both feet. In fact, to date I have yet to build anything 'real' for customers beyond a few internal infrastructure projects and quite a few sample applications.
For me personally, .NET Standard 2.0 and .NET Core 2.0 with their much bigger base library foot print and the real possibility of porting the majority of existing library code over to .NET Core really has been a deciding factor for me to start moving some of my existing full framework libraries that I've been using for as long as I have been using .NET to .NET Core 2.0. Being able to bring some of the tools I use to be productive over to .NET Core is actually pretty important factor to overcoming my reluctance to move into .NET Core. Nobody wants to rewrite code they already have just to get back to square one, but with .NET Core 2.0 it really looks like most code will migrate pretty easily.
This isn't just important to me personally, but I think this is a vital requirement for moving much of the support libraries that exist for .NET into .NET Core and providing the full featured eco-system that we've come to expect from .NET applications. Currently with .NET Core 1.x it's been hit or miss feature wise to feel confident you can actually make it through a project without getting stuck with some missing core feature you can't easily find and have to build from scratch. My feeling is that .NET Core 2.0 will change all that by making it possible for most libraries to be ported with minimal effort.
In this post I describe porting an existing full framework library to .NET Core 2.0 and multi-targeting the project to support .NET 4.5, 4.0 and .NET Standard 2.0. The porting process was even easier than I expected, although the tooling required a bit of patience to get on with.
What you need to follow along:
Note that currently there's no support for .NET Core 2.0/.NET Standard 2.0 in the release version of Visual Studio, and that's why the Preview install is required. You can install the minimal .NET and .NET Core payload for a lightish install, and the install is side by side with Visual Studio 2017 RTM so both work.
A key concept to the porting process is .NET Standard 2.0 and how it relates to .NET Core 2.0.
For those of you that don't know, .NET Standard is a specification that serves as a blue print for .NET runtime implementations. The standard specifies what base features the runtime has to implement to support it. .NET Standard describes the base API library - what we used to think of as the Base Class Library (BCL) in full framework that make up the core features of the platform.
.NET Standard is a standard not an implementation and it's up to the runtime to implement the features set forth in the standard. The logistics of this involve some runtime magic where each runtime provides a set of .NET Standard forwarding assemblies that map the .NET Standard APIs to the actual underlying APIs on the specific runtime.
For the purposes of this discussion, the salient point is that .NET Core 2.0 is an implementation of .NET Standard 2.0 which means that if I implement a .NET Standard 2.0 compliant DLL it will run on .NET Core 2.0. And any other platform like .NET 4.6.1, Xamarin, Mono, UWP and Unity all of which will eventually support .NET Standard 2.0. By targeting .NET Standard 2.0 I can insure that my DLL will run on any of the target platforms that .NET Standard supports.
The big win with .NET Standard is that it provides a common interface to consumers of a library, as well as an official guideline to the actual runtime implementers.
For Visual Studio purposes targeting .NET Standard for a class library is also what gives the new SDK project type that is required to make multi-targeting work.
I don't want to rehash all the details about how .NET Standard works here, but you can read my earlier blog post .NET Standard 2.0 - Making Sense of .NET Again for a more detailed discussion on just how that works.
The key takeaway for this post is that your .NET Applications can now target .NET Standard 2.0 in your class libraries (or applications) and can have a very reasonable expectation of interoperability for a number of platforms. In this post I'll talk about full framework .NET 4.5, 4.0 and .NET Core 2.
Putting it to a Test: Porting a .NET 4.5/4.0 Library
To really put this all into perspective I decided to move one of my libraries - Westwind.Utilities - to .NET Core 2.0 and in the process target .NET Standard 2.0, .NET 4.5 and .NET 4.0 all in a single project. Multi-targeting from a single project is an awesome feature that makes it possible to create a single .NET library project that can target multiple .NET Framework targets. Using a single project I can create binaries - and a NuGet package if desired - for multiple platforms.
Westwind.Utilities is a really old project that I've been using since the very early years of .NET and it's interesting in this context because it contains a large hodge-podge of functionality that touches a lot of different framework features in a single library. If I built this today I would probably have broken most of the features out into separate projects, but there's a lot of convenience in having these features I use in almost every project provided in a single package. Anyway, the point is this is very typical full framework legacy .NET code that was designed with no concept of .NET Core and makes a for a good example of what you're likely to find when you start porting full framework code to .NET Core 2 and later.
Creating a new .NET Standard Project
The first step for moving this library is to create a new .NET Standard Class Library:
This creates a new SDK style project using a csproj file. This is the new, more streamlined, MSBUILD based project format that uses the underlying dotnet command line tooling to build, test and publish your library. These projects can also target multiple runtime versions, and when compiled, output multiple versions of your assembly for each runtime. You can also optionally publish and create a Nuget package that includes all runtime versions. The new SDK format lets you configure NuGet attributes directly in the csproj file configuration.
I also set up a test project at the same time to move over the existing tests for the old project.
Multi-Target Projects in Visual Studio
When it is all said and done, here's the what the final ported project ends up looking like in Visual Studio:
Notice the three targets for .NET 4.5, 4.0 and .NET Standard 2.0 all living in the same project. You can also see the dependencies that each of the different runtime implementations are pulling in. .NET Core only shows the two packages (Json.net and SqlClient) I pulled in, while .NET 4.5 shows the specific assembly reference - both explicit assemblies and dependent assemblies (the ones with the lock in Solution Explorer).
The good news is that you can now have a single project with multiple targets with one single build step. Yay!
The bad news is that there's currently no visual tooling support for managing multi-target projects in Visual Studio and you have to deal with the
.csproj file directly to change targets or apply special target configuration settings.
To ram that point home, when I go to the project properties for the my class library project here's what I see:
Yup - no runtime target shows because the UI can't handle multiple frameworks (it only looks at
<TargetFrameworks>). In order to manage multiple frameworks you currently have to work directly with the
Luckily that is now a lot easier for a couple of reasons:
Implicit File Inclusion
The new .csproj format no longer explicitly needs to add every file to the project. Code files are now implicitly considered part of the project and so no longer need to be explicitly included in the project which drastically reduces the size and complexity of the project file as well as reducing the change churn in the file which is better for source control management. There are still overrides that let you specify custom behaviors for specific files or add files that need to be explicitly included or pushed out as content into the build folder. But for your base code files, they are considered included by default unless you tell the project otherwise.
Side by Side Editing
You can now easily edit the
.csproj file from Visual Studio while the project is still active. Most changes are immediately reflected in Visual Studio although in the current preview that behavior is still a little spotty and some things require an explicit project/solution reload.
Other Posts you might also like