blog post image
Andrew Lock avatar

Andrew Lock

~14 min read

Understanding .NET Core, NETStandard, .NET Core applications and ASP.NET Core

As anyone in the .NET community who hasn't been living under a rock will know, there're a lot of exciting things happening with .NET at the moment with the announcement of the open source, cross platform, .NET Core. However, partly due to the very open nature of its evolution, there have been a whole host of names associated with its development - vNext, ASP.NET 5, ASP.NET Core, .NET generations etc.

In this post I'm going to try and clarify some of the naming and terminology surrounding the evolution of the .NET framework. I'll discuss some of the challenges the latest iteration is attempting to deal with and how the latest developments aim to address these.

This is really for those that have seen some of the big announcements but aren't sure about the intricacies of this new framework and how it relates to the existing ecosystem, which was my situation before I really started digging into it all properly!

Hopefully by the end of this article you'll have a clearer grasp of the latest in .NET!

There is now a much clearer explanation of .NET Standard on the .NET blog, focusing on .NET Standard 2.0 - I recommend you check it out!

The .NET Framework today

For some desktop .NET developers today, in particular ASP.NET developers, there is only one .NET Framework - the 'Big', 'Full' framework that is Windows only, the latest stable release of which is up to 4.6.1. The Framework as a whole consists of several distinct layers, as shown below.

Outline of the .NET Framwork

The most important parts for this discussion are the Common Language Runtime (CRL) which converts IL to machine code; the Base Class Library (BCL) which provides fundamental classes such as primitives, collections and IO classes that are found in System.dll. System.Text etc.; and the Framework Libraries which is a superset of the BCL and provides the various app models, for example Windows Forms, ASP.NET and WPF. I've omitted the compilers and language components for now, as they are largely tangential to this discussion.

Another key aspect of the the full .NET platform is the fact is it centrally installed. This has many benefits such as allowing a single known location for services, and reducing the overall footprint on disk (installed once per system instead of once per application), however it has a few drawbacks which we'll discuss later.

While some developers may well be isolated to working solely with the 'full' .NET Framework, multi-platform developers and PCL library authors will be aware there are in fact multiple .NET platforms, of which the .NET Framework is but one. On windows, as well as the full .NET Framework, there is the Windows 8/8.1 platform and the Universal Windows Platform. On phones there's the Windows Phone 8.1 platform and the Windows Phone Silverlight platform, as well as the Xamarin platforms for iOS and Android. The list goes on (Mono, Silverlight, .NET CF, .NET Micro etc).

Each of these frameworks implement a different subsection of the Methods and Classes available in other frameworks, while often adding additional APIs, and in general are not interoperable.

API shape overlap between frameworks

Portable Class libraries

Originally, although multiple frameworks shared various APIs, there was no simple way of writing code to run on multiple platforms without a significant amount of Visual Studio-foo and preprocessor #ifdefs.

Portable Class Libraries (PCLs) were introduced to make the process of compiling code and sharing code across multiple platforms much simpler. They have significant tooling in Visual Studio to help authoring, and overall the solution worked well enough.

The Visual Studio 2015 PCL platform selection dialog

However the complexities of targeting multiple libraries and understanding the available APIs for each combination of platforms can be daunting.

The use of reference assemblies when creating and consuming PCLs

There are also some additional issues PCLs did not address.

Supporting new platforms

When a new platform is released, any existing PCL libraries that want to be used on that platform must be recompiled to support it, even if the API surface exposed by the new platform is entirely contained in the PCL library's supported platforms.

For example, imagine you create a PCL which supports the .NET Framework 4.5 and also Windows Phone 8.1. You use only the subset of APIs in common between the two platforms and you publish your library to NuGet with the expected descriptive moniker portable-net45+wpa81.

Suppose some time later, a new .NET Framework called '.NET Watch' is released which exposes exactly the same API surface as Windows Phone 8.1. While the new platform would be perfectly capable of running your PCL Foo.dll, it is blocked from doing so by the explicit moniker attached to it.

I'll cover Microsoft's solution to this a little later in the section on NETStandard.

Each framework is a fork

While PCLs allow a certain degree of convergence in the APIs between various .NET platforms, this requires a significant development from Microsoft for one key reason - each of the .NET platforms described previously is a separate implementation fork. Updating or adding an API for use on each platform means separately implementing the code required in each of the distinct forks. Clearly that's an expensive process, especially given the massive developer dependence on the framework classes being robust and reliable.

Say hello to .NET Core

At the end of 2014, at the connect() developer conference, a new .NET Framework was announced - the open source, cross-platform (Linux, Windows and OS X), .NET Core.

As discussed in this highly recommended video, .NET Core is essentially an umbrella term used to describe a whole host of developments, designed to address the issues discussed previously. The new .NET Core platform looks as follows:

Outline of the .NET Core framwework

Where previously Windows Store and the ASP.NET Core app models would have sat in completely separate stacks, they now share a common BCL, called CoreFX. CoreFX is not only a matching API surface for each model, it is exactly the same implementation, and is delivered via NuGet. It consists solely of normal MSIL assemblies, of the sort that you write in your applications, and that anyone can contribute to on GitHub.

A further benefit of delivering the BCL by NuGet is that it allows a so called pay-for-play model, in which the system is highly modular, and you only add those modules which are required, as opposed to always having a much larger list of libraries always available (as in the full .NET Framework). This model enables you to easily upgrade the framework of a single application, while leaving other existing installed applications unaffected. This is important in, for example, a web server scenario, where updating the centrally installed .NET Framework can currently cause rare compatibility issues.

At the bottom of the stack, there is a very thin layer consisting of the CoreCLR (cross platform) and .NET Native (Windows) runtimes, which contains low level types such as String and Int32. While these are different for the two (current) app models, they are very thin layers that will change rarely. Plus, even this is delivered via NuGet.

As an aside, and hopefully to avoid any confusion later if you stumble across it, there is actually already a framework called .NETCore which is the the underlying framework name and moniker (netcore) used in Windows Store Development, and shouldn't be confused with the majority of 'new' references to .NET Core!

NETStandard - an evolution of PCLs

So, CoreFX and the CoreCLR provide a subset of the .NET Framework APIs, as a highly modular, cross platform, open source framework with a single implementation. However this still doesn't address the other previously mentioned issue of PCLs - new frameworks are unsupported without compilation.

In order to address this, Microsoft are actively working on an another target framework, NETStandard which can be targeted for PCLs, and will allow new platforms that meet the required specifications to be supported without re-compilation. This is being actively developed based on a spec here, and I really suggest checking it out to get a fuller understanding of the platform. The key figure in the document is the figure reproduced below.

Target Platform NameAlias
.NET Platform Standardnetstandard1.01.11.21.31.41.5
.NET Corenetcoreapp1.0
.NET Frameworknet4.6.2
4.6.1
4.6
4.5.2
4.5.1
4.5
Universal Windows Platformuap10.0
Windowswin8.1
8.0
Windows Phonewpa8.1
Windows Phone Silverlightwp8.1
8.0
Mono/Xamarin Platforms*
Mono*

In essence, instead of targeting (as in the example from earlier) .NET Framework 4.5 and also Windows Phone 8.1, you would target the .NET Platform Standard which allows you to target those platforms by default. So, from the table, you would target .NET Platform Standard 1.1, as that is the highest platform to which both frameworks conform. By doing that, you would actually also get compatibility with any other framework in the table which also implements 1.1 for 'free' - in this case all platforms except Windows Phone Silverlight. Later, if the new '.Net Watch' framework is released and implements all the required libraries from .NET Platform Standard 1.1, then your library will just work, without any additional compilation, and will have the NuGet moniker netstandard1.1.

Note, the netstandard moniker is not yet supported on NuGet but according to the documentation, should be supported in v3.4. Also, note that the netstandard monikers are actually the renamed values of dotnet monikers from a previous implementation - netstandard1.0 corresponds with dotnet5.0, netstandard1.1 corresponds with dotnet5.1 etc, up to dotnet5.4.

Currently, the proposed NETStandard reference set of libraries/contracts that are supported includes pretty much every package in CoreFX, versioned appropriately. In order for a platform to be considered to support a version of NETStandard, it must implement a subset of these reference assemblies, though it does not necessarily need to support them all. You can therefore end up in a situation where you are targeting e.g netstandard1.4, but using a dll that is not implemented by a particular platform target even though it supports netstandard1.4. For this reason, and unlike previous PCLs, all package dependencies must be fully specified. Supporting the 'unimplemented' assembly situation today seems somewhat sketchy, but no doubt will be handled by tooling (Visual Studio) more gracefully later, in order to guide you through such situations. More details on using 'Guard Rails (supports)' can be found in the working spec.

.NET Core Applications

Finally, this brings me to .NET Core Applications, netcoreapp. If you've managed to get this far, hopefully you have a pretty good understanding of the new .NET Core landscape, and this is just the final piece of the puzzle. The FAQ in the documentation really explains it best:

A .NET Core application is an application that can run on any .NET Core runtime: CoreCLR (current), .NETNative (future).

So, just as a WinForms application targets the .NET Framework, and a Windows Phone App might target the Windows Phone Platform, so a .NET Standard application targets the .NET Core platform. The naming for this is all a bit jumbled due to reuse (you get the feeling they would really rather call it a .NET Core application but can't because of the already used netcore moniker), but it's really as simple as that. Update - as of RTM, and as pointed out by Mani Gandham, what were previously called .NET Standard Applications are now .NET Core Applications, which makes a lot more sense!

A .NET Core application, given it runs on .NET Core which will support a version of NETStandard, will likely predominantly consist of calls to NETStandard APIs. Therefore, the majority of the code can likely be shared with, for example, a .NET Framework application, as long as it supports the appropriate NETStandard version. However, it is important to note that a .NET Core Application cannot inherently be reused on other frameworks - it relies on the .NET Core runtime. The NETStandard target framework is an abstract set of contracts that must be implemented in a framework. Think of it like interfaces, classes and apps - the NETStandard framework provides the interface, while the .NET Framework and .NET Core provide the implementations, and WinForms and .NET Core Applications target those frameworks.

ASP.NET Core

So what can we build with .NET Core? Currently there are two options - you can build Windows Store Apps which use the .NET Native Runtime, or you can build ASP.NET Core 1.0 web apps. This new framework (previously called ASP.NET 5, and before that vNext), is a complete rewrite of the existing ASP.NET framework designed to be highly modular, with a number of best practices built in (e.g. dependency injection).

For developers with prior familiarity with ASP.NET development, on opening a new ASP.NET Core project you will be greeted with a sense of both refreshing familiarity and significant differences. No longer is there a web.config or global.asax, and in their place are startup.cs and project.json (among others).

Note: as of RC2, web.config is back (though settings will live in appsettings.json), and project.json is going away!

WebForms, VB, WebPages and SignalR are all a no-go currently, (though most of those are now on the road map). However, once you get past that initial setup hurdle, you are working with pretty much the same ASP.NET MVC you have been, just with some nice enhancements like tag helpers.

Although ASP.NET Core is designed to be used with .NET Core, ASP.NET Core applications can also target the full .NET Framework. In this case, you don't get the benefit of running cross platform but you do get a stable, robust framework, that is probably already deployed where your applications need to run! I'll be going into far more detail about ASP.NET in subsequent posts so I'll leave it at that for now.

Summary

.NET Core is a somewhat nebulous term for a new framework, runtime and standards. It's easy to get lost in all the new names, especially given how many iterations some of them went through to get to this stage. Given that RC2 has only just been announced, there will no doubt be more changes before this all finally settles down, but I'll try to keep this post updated with any changes. I've also intentionally left out details of the Dotnet CLI and the (now obsolete) DNX, DNVM and DNU for simplicity and as the tooling is in greater flux than the library and runtime aspect discussed. However, if you made it this far hopefully you now have a better grasp of the (suddenly cross-platform) landscape that is on the horizon for .NET development!

Further reading

Andrew Lock | .Net Escapades
Want an email when
there's new posts?