Announcing .NET Core 3 Preview 2

By Rich Lander [MSFT]

Today, we are announcing .NET Core 3 Preview 2. It includes new features in .NET Core 3.0 and C# 8, in addition to the large number of new features in Preview 1. ASP.NET Core 3.0 Preview 2  is also released today. C# 8 Preview 2 is part of .NET Core 3 SDK, and was also released last week with Visual Studio 2019 Preview 2.

Download and get started with .NET Core 3 Preview 2 right now on Windows, macOS and Linux.

In case you missed it, we made some big announcements with Preview 1, including adding support for Windows Forms and WPF with .NET Core, on Windows, and that both UI frameworks will be open source. We also announced that we would support Entity Framework 6 on .NET Core, which will come in a later preview. ASP.NET Core is also adding many features including Razor components.

Please provide feedback on the release in the comments below or at dotnet/core #2263. You can see complete details of the release in the .NET Core 3 Preview 2 release notes.

.NET Core 3 will be supported in Visual Studio 2019, Visual Studio for Mac and Visual Studio Code. Visual Studio 2019 Preview 2 was released last week and has support for C# 8. The Visual Studio Code C# Extension (in pre-release channel) was also just updated to support C# 8.

C# 8

C# 8 is a major release of the language, as Mads describes in Do more with patterns in C# 8.0, Take C# 8.0 for a spin and Building C# 8.0. In this post, I’ll cover a few favorites that are new in Preview 2.

Using Declarations

Are you tired of using statements that require indenting your code? No more! You can now write the following code, which attaches a using declaration to the scope of the current statement block and then disposes the object at the end of it.

Switch Expressions

Anyone who uses C# probably loves the idea of a switch statement, but not the syntax. C# 8 introduces switch expressions, which enable the following: terser syntax, returns a value since it is an expression, and fully integrated with pattern matching. The switch keyword is “infix”, meaning the keyword sits between the tested value (here, that’s o) and the list of cases, much like expression lambdas. The following examples use the lambda syntax for methods, which integrates well with switch expressions but isn’t required.

You can see the syntax for switch expressions in the following example:

There are two patterns at play in this example. o first matches with the Point type pattern and then with the property pattern inside the {curly braces}. The _ describes the discard pattern, which is the same as default for switch statements.

You can go one step further, and rely on tuple deconstruction and parameter position, as you can see in the following example:

In this example, you can see you do not need to define a variable or explicit type for each of the cases. Instead, the compiler can match the tuple being testing with the tuples defined for each of the cases.

All of these patterns enable you to write declarative code that captures your intent instead of procedural code that implements tests for it. The compiler becomes responsible for implementing that boring procedural code and is guaranteed to always do it correctly.

There will still be cases where switch statements will be a better choice than switch expressions and patterns can be used with both syntax styles.

Async streams

Async streams are another major improvement in C# 8. They have been changing with each preview and require that the compiler and the framework libraries match to work correctly. You need .NET Core 3.0 Preview 2 to use async streams if you want to develop with either Visual Studio 2019 Preview 2 or the latest preview of the C# extension for Visual Studio Code. If you are using .NET Core 3.0 Preview 2 at the commandline, then everything will work as expected.

IEEE Floating-point improvements

Floating point APIs are in the process of being updated to comply with IEEE 754-2008 revision. The goal of this floating point project is to expose all “required” operations and ensure that they are behaviorly compliant with the IEEE spec.

Parsing and formatting fixes:

  • Correctly parse and round inputs of any length.
  • Correctly parse and format negative zero.
  • Correctly parse Infinity and NaN by performing a case-insensitive check and allowing an optional preceding + where applicable.

New Math APIs:

  • BitIncrement/BitDecrement — corresponds to the nextUp and nextDown IEEE operations. They return the smallest floating-point number that compares greater or lesser than the input (respectively). For example, Math.BitIncrement(0.0) would return double.Epsilon.
  • MaxMagnitude/MinMagnitude — corresponds to the maxNumMag and minNumMag IEEE operations, they return the value that is greater or lesser in magnitude of the two inputs (respectively). For example, Math.MaxMagnitude(2.0, -3.0) would return -3.0.
  • ILogB — corresponds to the logB IEEE operation which returns an integral value, it returns the integral base-2 log of the input parameter. This is effectively the same as floor(log2(x)), but done with minimal rounding error.
  • ScaleB — corresponds to the scaleB IEEE operation which takes an integral value, it returns effectively x * pow(2, n), but is done with minimal rounding error.
  • Log2 — corresponds to the log2 IEEE operation, it returns the base-2 logarithm. It minimizes rounding error.
  • FusedMultiplyAdd — corresponds to the fma IEEE operation, it performs a fused multiply add. That is, it does (x * y) + z as a single operation, there-by minimizing the rounding error. An example would be FusedMultiplyAdd(1e308, 2.0, -1e308) which returns 1e308. The regular (1e308 * 2.0) - 1e308 returns double.PositiveInfinity.
  • CopySign — corresponds to the copySign IEEE operation, it returns the value of x, but with the sign of y.

.NET Platform Dependent Intrinsics

We’ve added APIs that allow access to certain perf-oriented CPU instructions, such as the SIMD or Bit Manipulation instruction sets. These instructions can help achieve big performance improvements in certain scenarios, such as processing data efficiently in parallel. In addition to exposing the APIs for your programs to use, we have begun using these instructions to accelerate the .NET libraries too.

The following CoreCLR PRs demonstrate a few of the intrinsics, either via implementation or use:

For more information, take a look at .NET Platform Dependent Intrinsics, which defines an approach for defining this hardware infrastructure, allowing Microsoft, chip vendors or any other company or individual to define hardware/chip APIs that should be exposed to .NET code.

Introducing a fast in-box JSON Writer & JSON Document

Following the introduction of the JSON reader in preview1, we’ve added System.Text.Json.Utf8JsonWriter and System.Text.Json.JsonDocument.

As described in our System.Text.Json roadmap, we plan to provide a POCO serializer and deserializer next.

Utf8JsonWriter

The Utf8JsonWriter provides a high-performance, non-cached, forward-only way to write UTF-8 encoded JSON text from common .NET types like String, Int32, and DateTime. Like the reader, the writer is a foundational, low-level type, that can be leveraged to build custom serializers. Writing a JSON payload using the new Utf8JsonWriter is 30-80% faster than using the writer from Json.NET and does not allocate.

Here is a sample usage of the Utf8JsonWriter that can be used as a starting point:

The Utf8JsonWriter accepts IBufferWriter<byte> as the output location to synchronously write the json data into and you, as the caller, need to provide a concrete implementation. The platform does not currently include an implementation of this interface, but we plan to provide one that is backed by a resizable byte array. That implementation would enable synchronous writes, which could then be copied to any stream (either synchronously or asynchronously). If you are writing JSON over the network and include the System.IO.Pipelines package, you can leverage the Pipe-based implementation of the interface called PipeWriter to skip the need to copy the JSON from an intermediary buffer to the actual output.

You can take inspiration from this sample implementation of IBufferWriter<T>. The following is a skeleton array-backed concrete implementation of the interface:

JsonDocument

In preview2, we’ve also added System.Text.Json.JsonDocument which was built on top of the Utf8JsonReader. The JsonDocument provides the ability to parse JSON data and build a read-only Document Object Model (DOM) that can be queried to support random access and enumeration. The JSON elements that compose the data can be accessed via the JsonElement type which is exposed by the JsonDocument as a property called RootElement. The JsonElement contains the JSON array and object enumerators along with APIs to convert JSON text to common .NET types. Parsing a typical JSON payload and accessing all its members using the JsonDocument is 2-3x faster than Json.NET with very little allocations for data that is reasonably sized (i.e. < 1 MB).

Here is a sample usage of the JsonDocument and JsonElement that can be used as a starting point:

GPIO Support for Raspberry Pi

We added initial support for GPIO with Preview 1. As part of Preview 2, we have released two NuGet packages that you can use for GPIO programming.

The GPIO Packages includes APIs for GPIO, SPI, I2C and PWM devices. The IoT bindings package includes device bindings for various chips and sensors, the same ones available at dotnet/iot – src/devices.

Updated serial port APIs were announced as part of Preview 1. They are not part of these packages but are available as part of the .NET Core platform.

Local dotnet tools

Local dotnet tools have been improved in Preview 2. Local tools are similar to dotnet global tools but are associated with a particular location on disk. This enables per-project and per-repository tooling. You can read more about them in the .NET Core 3.0 Preview 1 post.

In this preview, we have added 2 new commands:

  • dotnet new tool-manifest
  • dotnet tool list

To add local tools, you need to add a manifest that will define the tools and versions available. The dotnet new tool-manifest command automates creation of the manifest required by local tools. After this file is created, you can add tools to it via dotnet tool install <packageId>.

The command dotnet tool list lists local tools and their corresponding manifest. The command dotnet tool list -g lists global tools.

There was a change in .NET Core Local Tools between .NET Core 3.0 Preview 1 and .NET Core 3.0 Preview 2. If you tried out local tools in Preview 1 by running a command like dotnet tool restore or dotnet tool install, you need to delete your local tools cache folder before local tools will work correctly in Preview 2. This folder is located at:

On mac, Linux: rm -r $HOME/.dotnet/toolResolverCache

On Windows: rmdir /s %USERPROFILE%\.dotnet\toolResolverCache

If you do not delete this folder, you will receive an error.

Assembly Unloadability

Assembly unloadability is a new capability of AssemblyLoaderContext. This new feature is largely transparent from an API perspective, exposed with just a few new APIs. It enables a loader context to be unloaded, releasing all memory for instantiated types, static fields and for the assembly itself. An application should be able to load and unload assemblies via this mechanism forever without experiencing a memory leak.

We expect this new capability to be used for the following scenarios:

  • Plugin scenarios where dynamic plugin loading and unloading is required.
  • Dynamically compiling, running and then flushing code. Useful for web sites, scripting engines, etc.
  • Loading assemblies for introspection (like ReflectionOnlyLoad), although MetadataLoadContext (released in Preview 1) will be a better choice in many cases.

More information:

Assembly unloading requires significant care to ensure that all references to managed objects from outside a loader context are understood and managed. When the loader context is requested to be unloaded, any outside references need to have been unreferenced so that the loader context is self-consistent only to itself.

Assembly unloadability was provided in the .NET Framework by Application Domains (AppDomains), which are not supported with .NET Core. AppDomains had both benefits and limitations compared to this new model. We consider this new loader context-based model to be more flexible and higher performance when compared to AppDomains.

Windows Native Interop

Windows offers a rich native API, in the form of flat C APIs, COM and WinRT. We’ve had support for P/Invoke since .NET Core 1.0, and have been adding the ability to CoCreate COM APIs and Activate WinRT APIs as part of the .NET Core 3.0 release. We have had many requests for these capabilities, so we know that they will get a lot of use.

Late last year, we announced that we had managed to automate Excel from .NET Core. That was a fun moment. You can now try this same demo yourself with the Excel demo sample. Under the covers, this demo is using COM interop features like NOPIA, object equivalence and custom marshallers.

Managed C++ and WinRT interop are coming in a later preview. We prioritized getting COM interop built first.

WPF and Windows Forms

The WPF and Windows Forms teams opened up their repositories, dotnet/wpf and dotnet/winforms, respectively, on December 4th, the same day .NET Core 3.0 Preview 1 was released. Much of the last month, beyond holidays, has been spent interacting with the community, merging PRs, and responding to issues. In the background, they’ve been integrating WPF and Windows Forms into the .NET Core build system, including adopting the Arcade SDK. Arcade is an MSBuild SDK that exposes functionality which is needed to build the .NET Platform. The WPF team will be publishing more of the WPF source code over the coming months.

The same teams have been making a final set of changes in .NET Framework 4.8. These same changes have also been added to the .NET Core versions of WPF and Windows Forms.

Visual Studio support

Desktop development on .NET Core 3 requires Visual Studio 2019. We added the WPF and Windows Forms templates to the New Project Dialog to make it easier starting your new applications without using the command line.

The WPF and Windows Forms designer teams are continuing to work on an updated designer for .NET Core, which will be part of a Visual Studio 2019 Update.

MSIX Deployment for Desktop apps

MSIX is a new Windows app package format. It can be used to deploy .NET Core 3 desktop applications to Windows 10.

The Windows Application Packaging Project, available in Visual Studio 2019 preview 2, allows you to create MSIX packages with self-contained .NET Core applications.

Note: The .NET Core project file must specify the supported runtimes in the <RuntimeIdentifiers> property:

<RuntimeIdentifiers>win-x86;win-x64</RuntimeIdentifiers>

Install .NET Core 3.0 Previews on Linux with Snap

Snap is the preferred way to install and try .NET Core previews on Linux distributions that support Snap. At present, only X64 builds are supported with Snap. We’ll look at supporting ARM builds, too.

After configuring Snap on your system, run the following command to install the .NET Core SDK 3.0 Preview SDK.

sudo snap install dotnet-sdk --beta --classic

When .NET Core in installed using the Snap package, the default .NET Core command is dotnet-sdk.dotnet, as opposed to just dotnet. The benefit of the namespaced command is that it will not conflict with a globally installed .NET Core version you may have. This command can be aliased to dotnet with:

sudo snap alias dotnet-sdk.dotnet dotnet

Some distros require an additional step to enable access to the SSL certificate. See our Linux Setup for details.

Platform Support

.NET Core 3 will be supported on the following operating systems:

  • Windows Client: 7, 8.1, 10 (1607+)
  • Windows Server: 2012 R2 SP1+
  • macOS: 10.12+
  • RHEL: 6+
  • Fedora: 26+
  • Ubuntu: 16.04+
  • Debian: 9+
  • SLES: 12+
  • openSUSE: 42.3+
  • Alpine: 3.8+

Chip support follows:

  • x64 on Windows, macOS, and Linux
  • x86 on Windows
  • ARM32 on Windows and Linux
  • ARM64 on Linux

For Linux, ARM32 is supported on Debian 9+ and Ubuntu 16.04+. For ARM64, it is the same as ARM32 with the addition of Alpine 3.8. These are the same versions of those distros as is supported for X64. We made a conscious decision to make supported platforms as similar as possible between X64, ARM32 and ARM64.

Docker images for .NET Core 3.0 are available at microsoft/dotnet on Docker Hub. We are in the process of adopting Microsoft Container Registry (MCR). We expect that the final .NET Core 3.0 images will only be published to MCR.

Closing

Take a look at the .NET Core 3.0 Preview 1 post if you missed that. It includes a broader description of the overall release including the initial set of features, which are also included and improved on in Preview 2.

Thanks to everyone that installed .NET Core 3.0 Preview 1. We appreciate you trying out the new version and for your feedback. Please share any feedback you have about Preview 2.