Exploring .NET 8 Preview 3: The 7 Features You Need to Know
Tags: csharp, dotnet, programming, news
Published: 2023-04-19 12:57:07

Buckle up, developers, because Microsoft is back at it again with the release of .NET 8 Preview 3! Hot on the heels of the previous [preview 1](https://www.bytehide.com/blog/dotnet-8-preview-1/) and [preview 2](https://www.bytehide.com/blog/dotnet-8-preview-2/), this latest iteration promises to deliver even more enhancements and exciting new features to the ever-evolving .NET ecosystem.

In this article, we’ll be diving into the fresh updates and optimizations that Preview 3 has to offer, so grab your favorite coding beverage (coffee in my case☕) and let’s explore the cutting-edge advancements of .NET 8 Preview 3 together.

## Simpler Output Path

Building .NET apps can be a wild ride, and over time, Microsoft have all gotten used to that tangled web of output paths for our beloved build artifacts. You know the drill: `bin`, `obj`, `publish`, and all those quirky arrangements. Not to mention the per-project directories! But hey, Microsoft has been listening, and it’s clear that it’s time for a change.

So, buckle up, because the .NET SDK has whipped up a [spiffy new option](https://github.com/dotnet/sdk/pull/29599) to have an easier output path structure. Here’s the lowdown:

-   All build outputs gather ’round in one cozy spot.
-   Each project gets its own little corner in that happy place.
-   The whole shebang is flattened to a max of [three depth levels](https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-preview-3/#simplified-output-path).

Ready to dive in? Set the `UseArtifactsOutput` property in a `Directory.Build.props` file, and you’ll be off to the races. Just run `dotnet new buildprops` in your repo’s root, tweak the generated `Directory.Build.props` file, and add:

```csharp
<UseArtifactsOutput>true</UseArtifactsOutput>
```

And there you have it! All your build output will now snuggle up in the `.artifacts` directory in your repo’s root. Not a fan of the default? No sweat, just change the `ArtifactsPath` property in your `Directory.Build.props` file to your heart’s desire.

The new `.artifacts` directory will look like this: `<ArtifactsPath>\<Type of Output>\<Project Name>\<Pivots>`. So, you’ll have:

-   Make order of the compilation outputs. These would be done in different categories. We could find categories such as NuGet packages, publishet applications or binaries.
-   A tidy way to sort out build options, like `Configuration` and `RuntimeIdentifier`.

They believe this streamlined structure will make your life a whole lot easier and pave the way for future improvements. Plus, it’ll be a breeze for tools to handle build outputs.

## Time for Spring Cleaning with dotnet workload clean

Have you ever encountered extra workload packages after updating .NET SDK or Visual Studio? It’s a bumpy ride, and sometimes, things get left behind. But don’t panic – and please, don’t manually delete those directories!

Microsoft is excited to introduce a useful new command that helps clean up the leftover mess:

```csharp
dotnet workload clean
```

Got workload woes? Give `dotnet workload clean` a whirl to safely tidy up before trying again. This handy helper comes in two flavors:

### `dotnet workload clean`

It is somewhat similar to a [garbage collector](https://github.com/dotnet/designs/blob/main/accepted/2021/workloads/workload-installation.md#workload-pack-installation-records-and-garbage-collection) but also works for workloads that are based on MSI files. The tool is specifically designed to remove any leftover packages that are no longer needed after uninstalling previous versions of .NET SDK

And heads up! On the other hand, we have our friend Visual Studio, which manages the workloads. `Dotnet workload clean` will remind you to uninstall them via Visual Studio, not the .NET SDK CLI.

#### `dotnet workload clean --all`

This one’s a bit more intense. `workload clean --all` not only performs the function that a garbage collector would perform, in this case it is different. All packages that do not match the load facility and/or are not from Visual Studio would be cleaned up.

So, next time you find yourself in workload pack purgatory, give `dotnet workload clean` a try and watch your troubles disappear!

## Meet the Handy ValidateOptionsResultBuilder[](https://devblogs.microsoft.com/dotnet/announcing-dotnet-8-preview-3//#validateoptionsresultbuilder)

Say hello to the ValidateOptionsResultBuilder, your new best friend for creating a [`ValidateOptionsResult`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.options.validateoptionsresult) object! It makes implementing [`IValidateOptions.Validate(String, TOptions)`](https://learn.microsoft.com/dotnet/api/microsoft.extensions.options.ivalidateoptions-1.validate) a breeze.

The best part? You will be able to check several problems at the same time as this constructor is able to add several constructors!

Here’s a little taste of how it works:

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

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

// Reset the builder for a fresh start.
builder.Clear();
```

## Say Hello to the Configuration Binding Source Generator!

ASP.NET Core uses configuration providers to read data (key-value pairs) from sources like appsettings.json, environment variables, and Azure Key Vault.

The heart of this system is `ConfigurationBinder`, which maps configuration values to strongly-typed objects with `Bind` and `Get` methods. However, the current reflection-based approach has its drawbacks.

Fear not! Now, Microsoft adds a generator (which is AOT compliant and creates binding implementations) that is able to generate source code. The generator focuses on `Configure`, `Bind`, and `Get` calls.

Check out the example below to see the binder in action:

```csharp
// ... (sample code)

public class MyOptions
{
    // ... (property declarations)
}

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

To enable the generator, grab the latest preview of `Microsoft.Extensions.Configuration.Binder` and tweak your project file like so:

```csharp
<PropertyGroup>
    <EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>true</EnableMicrosoftExtensionsConfigurationBinderSourceGenerator>
</PropertyGroup>
```

## Mix & Match Architectures with .NET Container Images!

With both Arm64 and x64 machines now widely used, Docker supports building multi-platform images that work across different environments. Microsoft has finally managed to develop a new pattern for .NET image architectures just to help!

What if you imagine for a moment that you have a computer with an Apple Mac and what you need is to point to an x64 service that is currently in the azure cloud? No sweat! Just use the `--platform` switch:

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

To make the SDK run faster and more compatibly on your local machine, update one line in your Dockerfile:

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

Microsoft have also spruced up the SDK to support `$TARGETARCH` values and added the `-a` argument on restore. Check it out:

```docker
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 cool method lets you whip up optimized apps that get along swimmingly with Docker. Neat, huh? `--platform` values. Take a peek at this [sample](https://github.com/dotnet/dotnet-docker/blob/main/samples/aspnetapp/Dockerfile.alpine-non-root) to see the pattern in action!

## A Handy Trick for Non-Root User UID

This time Microsoft has discovered that the runAsNonRoot test requires that the same user that is configuring the container be set by UID. But, Microsoft didn’t want you to memorize a special number for tons of Dockerfiles.

Here you can check this [Dockerfile](https://github.com/dotnet/dotnet-docker/blob/e5bc76bca49a1bbf9c11e74a590cf6a9fe9dbf2a/samples/aspnetapp/Dockerfile.alpine-non-root#L27) example out how it can be used:

```docker
USER $APP_UID
```

If you want to build a Docker container image with `USER` like this, the container metadata shows:

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

Also you can check how the environment variable is defined:

```docker
$ 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
```