Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

What's new in .NET 8 Preview 3 #8135

Open
3 tasks
leecow opened this issue Jan 24, 2023 · 9 comments
Open
3 tasks

What's new in .NET 8 Preview 3 #8135

leecow opened this issue Jan 24, 2023 · 9 comments

Comments

@leecow
Copy link
Member

leecow commented Jan 24, 2023

What's new in .NET 8 Preview 3

This issue is for teams to highlight work for the community that will release in .NET 8 Preview 3

To add content, use a new conversation entry. The entry should include the team name and feature title as the first line shown in the template below.

Required

## Team Name: Feature title

[link to the tracking issue or epic item for the work]

Tell the story of the feature and anything the community should pay particular attention 
to be successful in using the feature.

Optional

Below are three additional items to consider. These will help the .NET 8 blog team and the community throughout the release.

  • Link to documentation.
    • Where can we send people to learn more about the feature?
  • Link to where you'd like people to provide feedback for this feature.
    • Where can people best comment on the direction of this feature?
  • Whether you would like assistance from the .NET 8 writers to craft a story for this feature.

Index of .NET 8 releases

Preview 1: #8133
Preview 2: #8134
Preview 3: #8135
Preview 4: #8234
Preview 5: https://github.com/dotnet/core/issues
Preview 6: https://github.com/dotnet/core/issues
Preview 7: https://github.com/dotnet/core/issues
RC 1: https://github.com/dotnet/core/issues
RC 2: https://github.com/dotnet/core/issues

@leecow leecow changed the title What's new in .NET Preview 3 [WIP] What's new in .NET 8 Preview 3 [WIP] Jan 24, 2023
@baronfel
Copy link
Member

baronfel commented Mar 24, 2023

SDK: Simplified Output Path format for builds

.NET applications can be built in many different ways, and as a result users of the platform have gotten familiar with a very deep and complex set of output paths for different build artifacts. Folders like bin, obj, publish, and the many different permutations and arrangements of those are muscle memory for many .NET developers. Similarly strong is the concept of per-project directories for these outputs. However, over time we've gotten feedback from both new and long-standing .NET users that this layout is difficult to reason about (because the layout can change drastically via relatively simple MSBuild changes) and difficult for tooling to anticipate (because the per-project layout makes it hard to be sure that you've gotten the outputs for every project).

To address both of these challenges and make the build outputs easier to use and more consistent, the .NET SDK team has introduced an option that creates a more unified, simplified output path structure. The new output path focuses on

  • gathering all of the build outputs in a common location,
  • separating the build outputs by project under this common location, and
  • flatting the overall build output layouts to a maximum of three levels deep

To opt into the new output path layout, you need to set the UseArtifactsOutput property in a Directory.Build.props file. The easiest way to get started is to run dotnet new buildprops in the root of your repository, and then open the generated Directory.Build.props file and add the following to the PropertyGroup in that file:

<UseArtifactsOutput>true</UseArtifactsOutput>

From this point on, build output for all projects will be placed into the .artifacts directory in the repository root. This is configurable though - just set the ArtifactsPath property in your Directory.Build.props file to any directory you prefer. If you don't want to use .artifacts as the default, we'd love to hear that feedback on the design discussion. The layout of the .artifacts directory will be of the form <ArtifactsPath>\<Type of Output>\<Project Name>\<Pivots>, where

  • Type of Output is used to group different categories of build outputs like binaries, intermediate/generated files, published applications, or NuGet packages, and
  • Pivots is used to flatten out all of the different knobs that used to differentiate builds, like Configuration and RuntimeIdentifier
  • .artifacts\bin\debug - The build output path for a simple project when you run dotnet build
  • .artifacts\obj\debug - The intermediate output path for a simple project when you run dotnet build
  • .artifacts\bin\MyApp\debug_net8.0 - The build output path for the net8.0 build of a multi-targeted project
  • .artifacts\publish\MyApp\release_linux-x64 - The publish path for a simple app when publishing for linux-x64
  • .artifacts\package\release - The folder where the release .nupkg will be created for a project

We think that this unified output structure addresses concerns that we've heard from our users and gives us a foundation we can build on for the future. The Type of Output and Pivots sections enable us to add new kinds of outputs, or new kinds of builds, without drastically changing the layout in the future. Anchoring all of the outputs in a single folder makes it easier for tooling to include, ignore, or manipulate the build outputs. Please give the new layout a try with this preview, and give us feedback! We'd love to hear how you use it.

@baronfel
Copy link
Member

baronfel commented Mar 24, 2023

SDK: Add dotnet workload clean command

Over the course of several .NET SDK and Visual Studio updates it's possible for workload packs (the actual units of functionality, tools, and templates that a workload is comprised of) to be left behind. This can happen for a number of reasons, but in every case it's confusing for end users. Some users go so far as to manually delete workload directories from their SDK install locations, which the SDK team really doesn't recommend! Instead of that drastic measure, this preview we've implemented a new command to help clean up leftover workload packs: dotnet workload clean. clean has two modes of operation:

dotnet workload clean

Runs workload garbage collection for either file-based or MSI-based workloads. Under this mode, garbage collection behaves as normal, cleaning only the orphaned packs themselves. This means it will clean up orphaned packs from uninstalled versions of the .NET SDK or packs where installation records for the pack no longer exist. This will only impact packs of feature bands below, or at the current feature band number of the SDK running workload clean, as the overall workload pack structural design may change in later versions.

If Visual Studio is installed and has been managing workloads as well, dotnet workload clean will list all Visual Studio Workloads installed on the machine and warn that they must be uninstalled via Visual Studio instead of the .NET SDK CLI. This is to provide clarity as to why some workloads are not cleaned/uninstalled after running dotnet workload clean.

dotnet workload clean --all

Unlike workload clean, workload clean --all runs garbage collection irregularly, meaning that it cleans every existing pack on the machine that is not from VS, and is of the current SDK workload installation type. (Either File-Based or MSI-Based.)

Because of this, it also removes all workload installation records for the running .NET SDK feature band and below. workload clean does not yet remove installation records, as the manifests are currently the only way to map a pack to the workload ID, but the manifest files may not exist for orphaned packs.


Next time you encounter issues managing workloads, consider using workload clean to safely restore to a known-good state before trying again. And as always, we want to hear your feedback about workloads at the .NET SDK issues.

@baronfel
Copy link
Member

baronfel commented Mar 24, 2023

SDK: Misc. Breaking Changes

The .NET SDK tries to make changes without causing behavioral breaks, but sometimes that's either not possible or the breaking change is just obviously correct. The first breaking change we'll talk about for the SDK in preview 3 is the latter category. The .NET SDK has a few ways to change the locale of the dotnet CLI, and for all of them actually setting the language would cause the CLI to set UTF-8 as the encoding of the standard input and output streams, even if the host system didn't support them. That's just wrong, so we changed that to only happen if the host system supports UTF-8. The actual breaking part of this though is that the dotnet CLI also persisted those codepage changes in the terminal session after the application closed, so any CLI applications running after the dotnet CLI would be running in an encoding other than what the user may have actually intended. If your application relied on this ill-behavior on our part, you should look at using commands like chcp on Windows to change the codepage encodings to what you need.

@tarekgh
Copy link
Member

tarekgh commented Mar 26, 2023

Introducing ValidateOptionsResultBuilder

To implement the IValidateOptions.Validate(String, TOptions) method effectively, you need to create a ValidateOptionsResult instance that describes the validation result. However, generating a high-quality instance can be cumbersome and may result in layered validation errors. This means that if there are multiple errors, the validation process often stops at the first error. As a result, you may have to go through an iterative process of editing and running the code until all the errors are resolved.

The ValidateOptionsResultBuilder has been introduced to facilitate the creation of a ValidateOptionsResult object. This builder allows for the accumulation of multiple errors.

Usage example

ValidateOptionsResultBuilder builder = new();
builder.AddError("Error: invalid operation code");
builder.AddResult(ValidateOptionsResult.Fail("Invalid request parameters"));
builder.AddError("Malformed link", "Url");

// Build ValidateOptionsResult object has accumulating multiple errors.
ValidateOptionsResult result = builder.Build();

// Reset the builder to allow using it in new validation operation.
builder.Clear();

@layomia
Copy link

layomia commented Mar 27, 2023

Introducing the configuration binding source generator

dotnet/runtime#82179

Application configuration in ASP.NET Core is performed using one or more configuration providers. Configuration providers read data (as key-value pairs) from a variety of sources such as settings files (e.g. appsettings.json), environment variables, Azure Key Vault etc.

At the core of this mechanism is ConfigurationBinder, an extension class that provides Bind and Get methods that map configuration values (IConfiguration instances) to strongly-typed objects. Bind takes an instance, while Get creates one on behalf of the caller. The current approach currently uses reflection which causes issues for trimming and Native AOT.

In .NET 8, as a replacement to reflection, we wish to ship a source generator that generates reflection free and AOT friendly binding implementations. The generator probes for Configure, Bind, and Get calls that we can retrieve type info from.

Here's example of code that users write to invoke the binder:

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

WebApplicationBuilder builder = WebApplication.CreateBuilder(args);
IConfigurationSection section = builder.Configuration.GetSection("MyOptions");

// !! Configure call - to be replaced with source-gen'd implementation
builder.Services.Configure<MyOptions>(section);

// !! Get call - to be replaced with source-gen'd implementation
MyOptions options0 = section.Get<MyOptions>();

// !! Bind call - to be replaced with source-gen'd implementation
MyOptions options1 = new MyOptions();
section.Bind(myOptions1);

WebApplication app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();

public class MyOptions
{
    public int A { get; set; }
    public string S { get; set; }
    public byte[] Data { get; set; }
    public Dictionary<string, string> Values { get; set; }
    public List<MyClass> Values2 { get; set; }
}

public class MyClass
{
    public int SomethingElse { get; set; }
}

When the generator is enabled in a project, the generated methods are implicitly picked, by the compiler, over the pre-existing reflection-based framework implementations. To enable the source generator, download the latest preview version of the the Microsoft.Extensions.Configuration.Binder NuGet package. The generator is off by default. To use it, add the following property to your project file:

<PropertyGroup>
    <EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>true</EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>
</PropertyGroup>

In preview 4, we will add the enabling mechanism to the .NET SDK so that the NuGet package reference is not required to use the source generator.

Please try the feature and report any issues at https://github.com/dotnet/runtime/issues.

Stretch info

  • Perf benchmarks vs reflection implementation

@richlander
Copy link
Member

richlander commented Mar 29, 2023

Building multi-platform container images

It is now common to use both Arm64 and x64 machines on a regular basis. x64 machines have been around for decades, however, Arm64 dev machines (like Apple Macs) and Arm64 cloud nodes are relatively new. Docker supports using and building multi-platform images that work across multiple environments. We've developed a new pattern that enables you to mix and match architectures with the .NET images you build.

Imagine you are on an Apple Mac and want to target an x64 cloud service in Azure. You can build the image by using the --platform switch as follows.

docker build --pull -t app --platform linux/amd64 .

Using the new pattern, you'd update just one line in your Dockerfile (the build stage):

FROM --platform=$BUILDPLATFORM mcr.microsoft.com/dotnet/sdk:8.0-preview-alpine AS build

That line enables the SDK to run on the local machine architecture, which will make it run faster and compatibly (since .NET doesn't support QEMU). This line didn't require a change in .NET but is a useful Docker feature.

We also updated the SDK (in Preview 3) to support $TARGETARCH values and to add the -a argument on restore. You can see that in the following example.

RUN dotnet restore -a $TARGETARCH

# copy everything else and build app
COPY aspnetapp/. .
RUN dotnet publish -a $TARGETARCH --self-contained false --no-restore -o /app

This approach enables producing more optimized apps in a way that integrates well with the Docker --platform values.

This sample demonstrates the pattern. You can also try it if you clone that repo.

Improving multi-platform container support goes into much more detail on this topic.

@richlander
Copy link
Member

richlander commented Mar 29, 2023

Environment Variable for non-root user UID value

We've added an environment variable for the UID for the non-root user that we added in Preview 1. We realized that the Kubernetes runAsNonRoot test required that the container user be set via UID not name. At the same time, we wanted to avoid developers needed to apply a number across (collectively) thousands of Dockerfiles. Instead, we are exposing that value -- 64198 in an environment variable.

You can see that used in this Dockerfile:

USER $APP_UID

That's the pattern we recommend for .NET 8.

When you build a container image when USER is defined like that, you will see the following in container metadata.

$ docker inspect app | jq .[-1].Config.User
"64198"

You can see how this environment variable is defined.

$ docker run --rm -it mcr.microsoft.com/dotnet/runtime bash -c "export | grep APP"
declare -x APP_UID="64198"
$ docker run --rm -it mcr.microsoft.com/dotnet/runtime cat /etc/passwd | tail -n 1
app:x:64198:64198::/home/app:/bin/sh

@JulieLeeMSFT
Copy link
Member

JulieLeeMSFT commented Apr 4, 2023

CodeGen

Community PRs (Many thanks to JIT community contributors!)

  • @DeepakRajendrakumaran enabled zmm registers to accelerate a subset of Vector512 methods, e.g., Create() and Zero using AVX512, in PR#80960.
  • @anthonycanino implemented light-weight K register ExtractMostSignificantBits for Vector512 in PR#80820.
  • @DeepakRajendrakumaran added some of Vector<T> APIs for Vector512 that would allow Vector<T> to select Vector512 on AVX-512 capable hardware, Issue#80814:
    • Load* and Store* in PR#82953
    • get_One, get_AllBitSet, createScalar(), createScalarUnsafe() in PR#83402.

Arm64

  • PR#83089 converts OR(condition, condition) to CCMP. It lets the JIT emit CCMP on arm64 for bitwise-or between relational comparisons.

PGO

General Optimizations

  • PR#79381 extends emitter peephole optimization and eliminated mov in more scenarios.
  • PR#82793 folded unreachable cases for switch in early phase of JIT, importer, and improved throughput up to 0.06%.
  • PR#82917 added System.Runtime.CompilerServices.Unsafe.BitCast for efficient type reinterpretation.
  • PR#81635 defers expansion of runtime lookups so that they can be better optimized.
  • PR#83274 unifies unrolling limits and chooses sensible (generally larger) values.

@richlander
Copy link
Member

richlander commented Apr 7, 2023

Breaking change: Multi-platform .NET 8 tags no longer support Windows containers

Multi-platform container images make it easy to use the same tags in multiple environments, leaving docker pull to pull content that matches the environment. Up until .NET 8, our multi-platform tags have included support for both Linux (Debian) and Windows (Nano Server). They will only reference Linux (Debian) images with .NET 8 and later. This change was made for .NET 8 Preview 3.

This change was made because Windows multi-platform tags don't work well with respect to selecting the best version. It is better to specify the Windows version you want. This change is discussed in much more detail in Switch multi-platform tags to Linux only.

Pulling a .NET 8 multi-platform tag will result in the following behavior, when "Windows Containers" mode is enabled.

>docker pull mcr.microsoft.com/dotnet/nightly/runtime:8.0-preview
8.0-preview: Pulling from dotnet/nightly/runtime
no matching manifest for windows/amd64 10.0.22621 in the manifest list entries

Going forward, you will need to use one of the following tags, for Windows

  • mcr.microsoft.com/dotnet/aspnet:8.0-preview-nanoserver-ltsc2022
  • mcr.microsoft.com/dotnet/aspnet:8.0-preview-nanoserver-1809
  • mcr.microsoft.com/dotnet/aspnet:8.0-preview-windowsservercore-ltsc2022
  • mcr.microsoft.com/dotnet/aspnet:8.0-preview-windowsservercore-ltsc2019

More information: dotnet/dotnet-docker#4549

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants