March 3, 2022
Using Postman with SignalR WebSockets Development
When I talk to .NET developers, I find that there’s still a lot of confusion about what .NET Standard is and how it relates to .NET Core.
In fact, this weekend I’ll be giving a talk at the Music City Tech conference in Nashville, Tennessee about just that. I thought I’d share some of the basics here, but if you’d like more details and can get yourself to Nashville, I hope you will join us this weekend!
First, a few definitions…
A couple years ago, Microsoft started creating .NET Core and on top of that, ASP.NET Core, originally known as ASP.NET vNext and ASP.NET 5. With .NET Core, Microsoft created it’s first truly cross-platform version of the .NET Framework that runs equally well on Mac, Linux, and Windows platforms.
The .NET Framework we’ve known and loved for years–the one that’s been shipping with Windows since XP. Over the years, it’s been adapted into several different flavors/platforms, including Mono, UWP, Silverlight, Windows 8, Windows Phone 8, and event .NET Micro. When there is a need to distinguish it from .NET Core, it is often called the full framework of just the .NET Framework. Especially because .NET Core and .NET Standard came about at the same time, one can be forgiven for thinking that .NET “Standard” might simply mean the plain-old, “standard” version of .NET that’s not .NET Core. But it doesn’t mean that.
When Microsoft created yet another flavor of .NET in .NET Core, they saw the need to be able to compile a library or package and reuse it across all the .NET platforms without requiring a recompile. They did it by creating a new standard (as in a measure, norm, or pattern) which platforms of .NET could either conform to or not. They called this standard the .NET Standard. The higher the .NET Standard version number gets, the greater the number of guaranteed APIs that will be implemented, but also, the fewer the number of platforms there will be that implement it. Or, to put it another way, the lower your standards are, the higher your backward-compatibility will be, and visa-versa.
If you’re familiar with the older ways we used to try to share code across flavors of the framework, you’ll recognize the name Portable Class Libraries (PCLs). Before .NET Standard, this was the way you would share code across different types of .NET projects. There were many problems with PCLs, but one of them was the way they specified their supported platforms. They did it by enumerating them, which meant each time there were new flavors of .NET, it would create many new permutations with every possible combination. It was almost impossible to keep track of an very difficult to use.
.NET Standard approaches the problem in a different way. Instead, it adds API surface area in concentric rings, like the growth rings of a tree. Each new version simple adds more APIs around the ones there were already there. Each .NET platform, then, either implements all of those APIs or not. If it doesn’t it’s compatible with that version of .NET Standard.
David Fowler uses the analogy of interfaces and implementation classes to explain what .NET Standard is in a very developer-friendly way, and I think it’s very apt.
But, perhaps the most simple way to understand .NET Standard is with a simple table that Microsoft publishes on the .NET Standard github project which I’ve placed below.
This table shows the .NET flavors/platforms listed on the left and the .NET Standard versions along the top. The version numbers in the cells where these two meet are the minimum version of that platform that implement the selected version of .NET Standard. For example, If you want to create a library that will be used on .NET Core 2.0 or above and from the full Framework 4.6.1 or above, you can do so by targeting .NET Standard 2.0. However, if you also want to be compatible with .NET 4.6, you’ll need your library to target .NET Standard 1.3, which will mean you’ll lose access to all the APIs added between Standard 1.3 and 2.0.
When you reference .NET Standard, you’re actually referencing a DLL call netstandard.dll, which has each type forwarded to the underlying platform’s implementation of that type.
[assembly:TypeForwardedToAttribute(typeof(PlatformX.Example))] public class Example ...
It’s pretty ingenious, really, and it create a very small netstandard.dll with no actual implementation of its own!
Unless you publish your own shared libraries or Nuget packages, the place you’re most likely to encounter .NET Standard is in the UI for Nuget when you’re referencing a package.
If we look at the current Newtonsoft.Json listing in Nuget, you can see what .NET platforms it targets, including .NET Standard 1.0, 1.3, and 2.0, as well as any other dependencies each version will take in order to work. Notice that the 1.0 and 1.3 have to take a dependency on external serialization libraries, and 2.0 does not? That’s because .NET Standard 2.0 was the first to include the System.Serialization namespace.
There’s a great, interactive implementation of the versions table shown above that was created by Immo Landwerth, Program Manager on the .NET team at Microsoft. You can play with it to get a better feeling for how .NET Standard versions map to different platform versions.