{"id":45158,"date":"2023-04-11T10:05:00","date_gmt":"2023-04-11T17:05:00","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/dotnet\/?p=45158"},"modified":"2024-12-13T14:16:28","modified_gmt":"2024-12-13T22:16:28","slug":"announcing-dotnet-8-preview-3","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-8-preview-3\/","title":{"rendered":"Announcing .NET 8 Preview 3"},"content":{"rendered":"<p><a href=\"https:\/\/dotnet.microsoft.com\/next\">.NET 8 Preview 3<\/a> is now available. It includes changes to build paths, workloads, Microsoft.Extensions, and containers. It also includes performance improvements in the JIT, for Arm64, and dynamic PGO. If you missed the March preview, you may want to read the <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-8-preview-2\/\">Preview 2<\/a> post.<\/p>\n<p>You can <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/8.0\">download .NET 8 Preview 3<\/a> for Linux, macOS, and Windows.<\/p>\n<ul>\n<li><a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/8.0\">Installers and binaries<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/dotnet-docker\/blob\/main\/documentation\/supported-tags.md\">Container images<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/tree\/main\/release-notes\/8.0\">Release notes<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/blob\/main\/release-notes\/8.0\/known-issues.md\">Known issues<\/a><\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/core\/issues\">GitHub issue tracker<\/a><\/li>\n<\/ul>\n<p>Check out what&#8217;s new in <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/check-out-csharp-12-preview\">C#<\/a>, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-core-updates-in-dotnet-8-preview-3\">ASP.NET Core<\/a>, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/tag\/entity-framework\/\">EF Core<\/a>, and <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-dotnet-maui-in-dotnet-8-preview-3\/\">.NET MAUI<\/a> in the Preview 3 release. Stay current with <a href=\"https:\/\/learn.microsoft.com\/dotnet\/core\/whats-new\/dotnet-8\">What&#8217;s New in .NET 8<\/a>. <a href=\"https:\/\/learn.microsoft.com\/dotnet\/fundamentals\/\">.NET Docs<\/a> will be updated throughout the release.<\/p>\n<p>.NET 8 has been tested with 17.6 Preview 3. If you want to try .NET 8 with the Visual Studio family of products, we recommend that you use the <a href=\"https:\/\/visualstudio.com\/preview\">preview channel builds<\/a>. Visual Studio for Mac support for .NET 8 isn\u2019t yet available.<\/p>\n<p>Let&#8217;s take a look at some new features.<\/p>\n<p><a href=\"https:\/\/dotnet.microsoft.com\/next\"><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/dotnet\/wp-content\/uploads\/sites\/10\/2023\/04\/dotnet-8-preview-3.png\" alt=\"Download .NET 8 Preview 3\" \/><\/a><\/p>\n<h2>SDK<\/h2>\n<p>There were several improvements made to the SDK, as well as a breaking change. For more information about the breaking change, see <a href=\"https:\/\/github.com\/dotnet\/docs\/issues\/34471\">.NET SDK No Longer Changes Encoding Upon Exit<\/a>.<\/p>\n<p>The improvements made to the .NET SDK were the following:<\/p>\n<h3>Simplified output path<\/h3>\n<p>.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 <code>bin<\/code>, <code>obj<\/code>, <code>publish<\/code>, 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&#8217;ve gotten feedback from both new and long-standing .NET users that this layout is:<\/p>\n<ul>\n<li>Difficult to use because the layout can change drastically via relatively simple MSBuild changes.<\/li>\n<li>Difficult for tools to anticipate because the per-project layout makes it hard to be sure that you&#8217;ve gotten the outputs for every project.<\/li>\n<\/ul>\n<p>To address both of these challenges and make the build outputs easier to use and more consistent, the .NET SDK has <a href=\"https:\/\/github.com\/dotnet\/sdk\/pull\/29599\">introduced an option<\/a> that creates a more unified, simplified output path structure.<\/p>\n<p>The new output path focuses on:<\/p>\n<ul>\n<li>Gathering all of the build outputs in a common location.<\/li>\n<li>Separating the build outputs by project under this common location.<\/li>\n<li>Flattening the overall build output layouts to a maximum of three levels deep.<\/li>\n<\/ul>\n<p>To opt into the new output path layout, you need to set the <code>UseArtifactsOutput<\/code> property in a <code>Directory.Build.props<\/code> file. The easiest way to get started is to run <code>dotnet new buildprops<\/code> in the root of your repository, open the generated <code>Directory.Build.props<\/code> file, and then add the following to the <code>PropertyGroup<\/code> in that file:<\/p>\n<pre><code class=\"language-xml\">&lt;UseArtifactsOutput&gt;true&lt;\/UseArtifactsOutput&gt;<\/code><\/pre>\n<p>From this point on, build output for all projects will be placed into the <code>.artifacts<\/code> directory in the repository root. This is configurable &#8211; just set the <code>ArtifactsPath<\/code> property in your <code>Directory.Build.props<\/code> file to any directory you prefer. If you don&#8217;t want to use <code>.artifacts<\/code> as the default, we&#8217;d love to hear that feedback on <a href=\"https:\/\/github.com\/dotnet\/designs\/pull\/281\">the design discussion<\/a>.<\/p>\n<p>The layout of the <code>.artifacts<\/code> directory will be of the form <code>&lt;ArtifactsPath&gt;\\&lt;Type of Output&gt;\\&lt;Project Name&gt;\\&lt;Pivots&gt;<\/code>, where:<\/p>\n<ul>\n<li><code>Type of Output<\/code> is used to group different categories of build outputs like binaries, intermediate\/generated files, published applications, or NuGet packages, and<\/li>\n<li><code>Pivots<\/code> is used to flatten out all of the different options that are used to differentiate builds, like <code>Configuration<\/code> and <code>RuntimeIdentifier<\/code>.<\/li>\n<\/ul>\n<p>Some examples of paths that would be created under the new format are:<\/p>\n<ul>\n<li><code>.artifacts\\bin\\debug<\/code> &#8211; The build output path for a simple project when you run <code>dotnet build<\/code>.<\/li>\n<li><code>.artifacts\\obj\\debug<\/code> &#8211; The intermediate output path for a simple project when you run <code>dotnet build<\/code>.<\/li>\n<li><code>.artifacts\\bin\\MyApp\\debug_net8.0<\/code> &#8211; The build output path for the <code>net8.0<\/code> build of a multi-targeted project.<\/li>\n<li><code>.artifacts\\publish\\MyApp\\release_linux-x64<\/code> &#8211; The publish path for a simple app when publishing for <code>linux-x64<\/code>.<\/li>\n<li><code>.artifacts\\package\\release<\/code> &#8211; The folder where the release <code>.nupkg<\/code> will be created for a project.<\/li>\n<\/ul>\n<p>We think that this unified output structure addresses concerns that we&#8217;ve heard from users and gives us a foundation we can build on for the future. The <code>Type of Output<\/code> and <code>Pivots<\/code> sections enable us to add new kinds of outputs or builds without drastically changing the layout in the future. Anchoring all of the outputs in a single folder makes it easier for tools to include, ignore, or manipulate the build outputs. We&#8217;d love to hear about your experiences enabling and using the new layout in <a href=\"https:\/\/aka.ms\/dotnet\/sdk\/simplified-output-path-survey\">this SurveyMonkey survey<\/a><\/p>\n<h3><code>dotnet workload clean<\/code> command<\/h3>\n<p>Over the course of several .NET SDK and Visual Studio updates, it&#8217;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&#8217;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&#8217;t recommend! Instead of that drastic measure, in this preview we&#8217;ve <a href=\"https:\/\/github.com\/dotnet\/sdk\/pull\/30266\">implemented a new command<\/a> to help clean up leftover workload packs.<\/p>\n<p>The new command is:<\/p>\n<pre><code class=\"language-bash\">dotnet workload clean<\/code><\/pre>\n<p>Next time you encounter issues managing workloads, consider using <code>dotnet workload clean<\/code> to safely restore to a known-good state before trying again.<\/p>\n<p><code>clean<\/code> has two modes of operation, which are discussed next.<\/p>\n<h4><code>dotnet workload clean<\/code><\/h4>\n<p>Runs workload <a href=\"https:\/\/github.com\/dotnet\/designs\/blob\/main\/accepted\/2021\/workloads\/workload-installation.md#workload-pack-installation-records-and-garbage-collection\">garbage collection<\/a> for either file-based or MSI-based workloads. Under this mode, garbage collection behaves as normal, cleaning only the orphaned packs themselves.<\/p>\n<p>It will clean up orphaned packs from uninstalled versions of the .NET SDK or packs where installation records for the pack no longer exist. It will only do this for the given SDK version or older. If you have a newer SDK version installed, you will need to repeat the command.<\/p>\n<p>If Visual Studio is installed and has been managing workloads as well, <code>dotnet workload clean<\/code> 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 <code>dotnet workload clean<\/code>.<\/p>\n<h4><code>dotnet workload clean --all<\/code><\/h4>\n<p>Unlike <code>workload clean<\/code>, <code>workload clean --all<\/code> runs garbage collection irregularly, meaning that it cleans every existing pack on the machine that isn&#8217;t from Visual Studio and is of the current SDK workload installation type (either file-based or MSI-based).<\/p>\n<p>Because of this, it also removes all workload installation records for the running .NET SDK feature band and below. <code>workload clean<\/code> doesn&#8217;t 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.<\/p>\n<h2>Runtime<\/h2>\n<p>The following improvements were made in the runtime and libraries.<\/p>\n<h3>ValidateOptionsResultBuilder<\/h3>\n<p>The ValidateOptionsResultBuilder makes it easier to create of a <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/microsoft.extensions.options.validateoptionsresult\"><code>ValidateOptionsResult<\/code><\/a> object, which is required to implement <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/microsoft.extensions.options.ivalidateoptions-1.validate\"><code>IValidateOptions.Validate(String, TOptions)<\/code><\/a>. This builder allows you to accumulate multiple errors, so you can see all the issues at once and address them accordingly. With this new tool, you&#8217;ll save time and effort by streamlining the validation process.<\/p>\n<p>Here&#8217;s the usage example:<\/p>\n<pre><code class=\"language-csharp\">ValidateOptionsResultBuilder builder = new();\nbuilder.AddError(\"Error: invalid operation code\");\nbuilder.AddResult(ValidateOptionsResult.Fail(\"Invalid request parameters\"));\nbuilder.AddError(\"Malformed link\", \"Url\");\n\n\/\/ Build ValidateOptionsResult object has accumulating multiple errors.\nValidateOptionsResult result = builder.Build();\n\n\/\/ Reset the builder to allow using it in new validation operation.\nbuilder.Clear();<\/code><\/pre>\n<h3>Introducing the configuration binding source generator<\/h3>\n<p><a href=\"https:\/\/learn.microsoft.com\/aspnet\/core\/fundamentals\/configuration\/?view=aspnetcore-7.0\">Application configuration in ASP.NET Core<\/a> 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 (for example, <code>appsettings.json<\/code>), environment variables, Azure Key Vault etc.<\/p>\n<p>At the core of this mechanism is <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/microsoft.extensions.configuration.configurationbinder?view=dotnet-plat-ext-7.0\"><code>ConfigurationBinder<\/code><\/a>, an extension class that provides <code>Bind<\/code> and <code>Get<\/code> methods that map configuration values (<a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/microsoft.extensions.configuration.iconfiguration?view=dotnet-plat-ext-7.0\"><code>IConfiguration<\/code><\/a> instances) to strongly-typed objects. <code>Bind<\/code> takes an instance, while <code>Get<\/code> creates one on behalf of the caller. The current approach uses reflection which causes issues for trimming and Native AOT.<\/p>\n<p>In .NET 8, we are <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/82179\">using a source generator<\/a> that generates reflection-free and AOT-friendly binding implementations. The generator probes for <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/microsoft.extensions.dependencyinjection.optionsconfigurationservicecollectionextensions.configure?view=dotnet-plat-ext-7.0#microsoft-extensions-dependencyinjection-optionsconfigurationservicecollectionextensions-configure-1(microsoft-extensions-dependencyinjection-iservicecollection-microsoft-extensions-configuration-iconfiguration)\">Configure<\/a>, <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/microsoft.extensions.configuration.configurationbinder.bind?view=dotnet-plat-ext-7.0\">Bind<\/a>, and <a href=\"https:\/\/learn.microsoft.com\/dotnet\/api\/microsoft.extensions.configuration.configurationbinder.get?view=dotnet-plat-ext-7.0\">Get<\/a> calls that we can retrieve type info from.<\/p>\n<p>The following example shows code that invokes the binder:<\/p>\n<pre><code class=\"language-cs\">using Microsoft.AspNetCore.Builder;\nusing Microsoft.Extensions.Configuration;\nusing Microsoft.Extensions.DependencyInjection;\n\nWebApplicationBuilder builder = WebApplication.CreateBuilder(args);\nIConfigurationSection section = builder.Configuration.GetSection(\"MyOptions\");\n\n\/\/ !! Configure call - to be replaced with source-gen'd implementation\nbuilder.Services.Configure&lt;MyOptions&gt;(section);\n\n\/\/ !! Get call - to be replaced with source-gen'd implementation\nMyOptions options0 = section.Get&lt;MyOptions&gt;();\n\n\/\/ !! Bind call - to be replaced with source-gen'd implementation\nMyOptions options1 = new MyOptions();\nsection.Bind(myOptions1);\n\nWebApplication app = builder.Build();\napp.MapGet(\"\/\", () =&gt; \"Hello World!\");\napp.Run();\n\npublic class MyOptions\n{\n    public int A { get; set; }\n    public string S { get; set; }\n    public byte[] Data { get; set; }\n    public Dictionary&lt;string, string&gt; Values { get; set; }\n    public List&lt;MyClass&gt; Values2 { get; set; }\n}\n\npublic class MyClass\n{\n    public int SomethingElse { get; set; }\n}<\/code><\/pre>\n<p>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 <a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Extensions.Configuration.Binder\">Microsoft.Extensions.Configuration.Binder<\/a>. The generator is off by default. To use it, add the following property to your project file:<\/p>\n<pre><code class=\"language-csproj\">&lt;PropertyGroup&gt;\n    &lt;EnableMicrosoftExtensionsConfigurationBinderSourceGenerator&gt;true&lt;\/EnableMicrosoftExtensionsConfigurationBinderSourceGenerator&gt;\n&lt;\/PropertyGroup&gt;<\/code><\/pre>\n<p>In preview 4, we&#8217;ll add the enabling mechanism to the .NET SDK, so that the NuGet package reference isn&#8217;t required to use the source generator.<\/p>\n<h3>Native code generation<\/h3>\n<p>The following improvements were made to the JIT compiler:<\/p>\n<h4>Arm64<\/h4>\n<p>The following optimizations were made for the Arm64 architecture:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/83089\">PR#83089<\/a> converts <code>OR(condition, condition)<\/code> to <code>CCMP<\/code>. It lets the JIT emit <code>CCMP<\/code> on arm64 for bitwise-or between relational comparisons.<\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/83176\">PR #83176<\/a> optimized <code>x &lt; 0<\/code> and <code>x &gt;= 0<\/code> for arm64 and gave good improvements (<a href=\"https:\/\/github.com\/dotnet\/perf-autofiling-issues\/issues\/14422\">here<\/a>, <a href=\"https:\/\/github.com\/dotnet\/perf-autofiling-issues\/issues\/14416\">here<\/a> and <a href=\"https:\/\/github.com\/dotnet\/perf-autofiling-issues\/issues\/14430\">here<\/a>).<\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/82924\">PR #82924<\/a> optimized overflow checks for certain scenarios during division.<\/li>\n<\/ul>\n<h4>Profile guided optimization<\/h4>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/conversation-about-pgo\/\">Profile guided optimization<\/a> has been improved in this release.<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/82775\">runtime #82775<\/a> enabled interlocked profiling for basic block counts.<\/li>\n<li>The profile synthesis is supported in <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/82926\">runtime #82926<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/83068\">runtime #83068<\/a>, <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/83185\">runtime #83185<\/a>, and <a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/83567\">runtime #83567<\/a>.<\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/83002\">runtime #83002<\/a> enabled more intrinsics in Tier0, e.g., <code>get_IsValueType<\/code>.<\/li>\n<\/ul>\n<h4>General Optimizations<\/h4>\n<p>The following general performance changes were made:<\/p>\n<ul>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/79381\">runtime #79381<\/a> extends emitter peephole optimization and eliminated <code>mov<\/code> in more scenarios.<\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/82793\">runtime #82793<\/a> folded unreachable cases for <code>switch<\/code> in early phase of JIT, <code>importer<\/code>, and improved throughput up to 0.06%.<\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/82917\">runtime #82917<\/a> added <code>System.Runtime.CompilerServices.Unsafe.BitCast<\/code> for efficient type reinterpretation.<\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/81635\">runtime #81635<\/a> defers expansion of runtime lookups so that they can be better optimized.<\/li>\n<li><a href=\"https:\/\/github.com\/dotnet\/runtime\/pull\/83274\">runtime #83274<\/a> unifies unrolling limits and chooses sensible (generally larger) values.<\/li>\n<\/ul>\n<h2>Containers<\/h2>\n<p>We&#8217;re continuing to improve the capabilities and experience of using .NET in containers. In this release, we&#8217;re focused on security and targeting multiple architectures.<\/p>\n<p>For recent updates, see <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/securing-containers-with-rootless\/\">Secure your .NET cloud apps with rootless Linux Containers<\/a> and <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/updates-to-container-support-in-the-dotnet-sdk\/\">SDK Containers \u2013 Support for Authentication and Cross-architecture Builds<\/a>.<\/p>\n<p>To learn about a recent tagging-related change, see <a href=\"https:\/\/github.com\/dotnet\/dotnet-docker\/discussions\/4549\">Breaking change: Multi-platform .NET 8 tags no longer support Windows containers<\/a>.<\/p>\n<h3>Building multi-platform container images<\/h3>\n<p>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 <a href=\"https:\/\/learn.microsoft.com\/azure\/aks\/use-multiple-node-pools#add-an-arm64-node-pool\">Arm64 cloud nodes<\/a> are relatively new. Docker supports using and building <a href=\"https:\/\/docs.docker.com\/build\/building\/multi-platform\/\">multi-platform images<\/a> that work across multiple environments. We&#8217;ve developed a new pattern that enables you to mix and match architectures with the .NET images you build.<\/p>\n<p>Imagine you&#8217;re on an Apple Mac and want to target an x64 cloud service in Azure. You can build the image by using the <code>--platform<\/code> switch as follows.<\/p>\n<pre><code class=\"language-bash\">docker build --pull -t app --platform linux\/amd64 .<\/code><\/pre>\n<p>Using the new pattern, you&#8217;d update just one line in your Dockerfile (the build stage):<\/p>\n<pre><code class=\"language-dockerfile\">FROM --platform=$BUILDPLATFORM mcr.microsoft.com\/dotnet\/sdk:8.0-preview-alpine AS build<\/code><\/pre>\n<p>That line enables the SDK to run on the local machine architecture, which will make it run faster and compatibly (since .NET doesn&#8217;t support QEMU). This line didn&#8217;t require a change in .NET but is a useful Docker feature.<\/p>\n<p>We also updated the SDK (in Preview 3) to support <code>$TARGETARCH<\/code> values and to add the <code>-a<\/code> argument on restore. You can see that in the following example.<\/p>\n<pre><code class=\"language-dockerfile\">RUN dotnet restore -a $TARGETARCH\n\n# copy everything else and build app\nCOPY aspnetapp\/. .\nRUN dotnet publish -a $TARGETARCH --self-contained false --no-restore -o \/app<\/code><\/pre>\n<p>This approach enables producing more optimized apps in a way that integrates well with the Docker <code>--platform<\/code> values.<\/p>\n<p>This <a href=\"https:\/\/github.com\/dotnet\/dotnet-docker\/blob\/main\/samples\/aspnetapp\/Dockerfile.alpine-non-root\">sample<\/a> demonstrates the pattern. You can also try it if you clone that repo.<\/p>\n<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/improving-multiplatform-container-support\/\">Improving multi-platform container support<\/a> goes into much more detail on this topic.<\/p>\n<h3>Environment Variable for non-root user UID value<\/h3>\n<p>We&#8217;ve added an environment variable for the UID for the non-root user that we added in Preview 1. We realized that the <a href=\"https:\/\/kubernetes.io\/docs\/concepts\/security\/pod-security-standards\/#restricted\">Kubernetes <code>runAsNonRoot<\/code> test<\/a> required that the container user be set via UID not name. At the same time, we wanted to avoid developers needing to apply a special number across (collectively) thousands of Dockerfiles. Instead, we&#8217;re exposing that value &#8212; <code>64198<\/code> &#8212; in an environment variable.<\/p>\n<p>You can see that used in this <a href=\"https:\/\/github.com\/dotnet\/dotnet-docker\/blob\/e5bc76bca49a1bbf9c11e74a590cf6a9fe9dbf2a\/samples\/aspnetapp\/Dockerfile.alpine-non-root#L27\">Dockerfile<\/a>:<\/p>\n<pre><code class=\"language-dockerfile\">USER $APP_UID<\/code><\/pre>\n<p>That&#8217;s the pattern we recommend for .NET 8.<\/p>\n<p>When you build a container image when <code>USER<\/code> is defined like that, you will see the following in container metadata.<\/p>\n<pre><code class=\"language-bash\">$ docker inspect app | jq .[-1].Config.User\n\"64198\"<\/code><\/pre>\n<p>You can see how this environment variable is defined.<\/p>\n<pre><code class=\"language-bash\">$ docker run --rm -it mcr.microsoft.com\/dotnet\/runtime bash -c \"export | grep APP\"\ndeclare -x APP_UID=\"64198\"\n$ docker run --rm -it mcr.microsoft.com\/dotnet\/runtime cat \/etc\/passwd | tail -n 1\napp:x:64198:64198::\/home\/app:\/bin\/sh<\/code><\/pre>\n<p>We will go into more detail on this pattern in a post on Kubernetes soon.<\/p>\n<h2>Community spotlight (Erik Ejlskov Jensen)<\/h2>\n<p>.NET is powered by community and a worldwide team of amazing contributors. Our community spotlight this month shines on Erik Ejlskov Jensen, known to many as <a href=\"https:\/\/github.com\/ErikEJ\">@ErikEJ<\/a>. Erik contributes code, helps triage issues and reviews pull requests. He is the maintainer of the popular <a href=\"https:\/\/github.com\/ErikEJ\/EFCorePowerTools\">EF Power Tools<\/a> Visual Studio extension that provides tools to manage and reverse-engineer databases, generate migrations and visualize the data model.<\/p>\n<p><img decoding=\"async\" src=\".\/erikej.jpg\" alt=\"Erik Ejlskov Jensen\" \/><\/p>\n<p>In Erik&#8217;s own words:<\/p>\n<p><em>&#8220;Hi, I\u2019m Erik Ejlskov Jensen, a Principal Consultant at Delegate A\/S and live near Copenhagen in Denmark. I have been fortunate enough to have been awarded Microsoft MVP for 14 years in a row for my community contributions. I mainly work with data0driven .NET applications using SQL Server\/Azure SQL Database.<\/em><\/p>\n<p><em>Back in 2005, I worked at a small Danish company that specialized in Windows Mobile development, and I soon got deeply engaged in the SQL Server Compact embedded database that was part of that platform. Soon I was helping others on the MSDN forums with that product, and I launched an open source Visual Studio extension to help SQL Compact developers. It was hosted on a Microsoft owned open source web site named CodePlex.<\/em><\/p>\n<p><em>One of the first big Microsoft projects to embrace open source on CodePlex was Entity Framework (Classic) in 2013, and it included and provider for SQL Server Compact that I was using in some of my work. The provider was not as feature complete as I desired, and since it was now open source, I amazingly got some pull requests against it accepted. The team running the EF6 project at the time was very welcoming and helpful, and encouraged community feedback, also based on my SQL Server experience. Many of the team members then are still working on the EF Core project today.<\/em><\/p>\n<p><em>When the Entity Framework (EF) Core project was launched in 2016, I immediately dug in and created a provider for SQL Server Compact for EF Core 1 and 2.<\/em><\/p>\n<p><strong>Editor&#8217;s note:<\/strong> check out the <a href=\"https:\/\/marketplace.visualstudio.com\/items?itemName=ErikEJ.SQLServerCompactSQLiteToolbox\">SQLite and SQL Server Compact Toolbox<\/a><\/p>\n<p><em>In 2017 I launched the EF Core Power Tools Visual Studio extension, and I contributed several features to EF Core reverse engineering, partly for selfish reasons so I could improve EF Core Power Tools. Since then, I have regularly been providing feedback, documentation updates and pull requests for bug fixes and new features. It is always a pleasure interacting with the EF Core team, who all are extremely professional and experienced in open source software development, not least the &#8216;expectation alignment&#8217; part.&#8221;<\/em><\/p>\n<p>Know somebody who is contributing to .NET that we should feature in future posts? Nominate them at <a href=\"https:\/\/aka.ms\/net8contributor\">https:\/\/aka.ms\/net8contributor<\/a>.<\/p>\n<h2>Summary<\/h2>\n<p>.NET 8 Preview 3 contains exciting new features and improvements that would not be possible without the hard work and dedication of a diverse team of engineers at Microsoft and a passionate open-source community. We want to extend our <a href=\"https:\/\/dotnet.microsoft.com\/thanks\">sincere thanks to everyone who has contributed to .NET 8 so far<\/a>, whether it was through code contributions, bug reports, or providing feedback.<\/p>\n<p>Your contributions have been instrumental in the making .NET 8 Previews, and we look forward to continuing to work together to build a brighter future for .NET and the entire technology community.<\/p>\n<p>Curious about what is coming? <a href=\"https:\/\/dotnet.microsoft.com\/next\">Go see for yourself what&#8217;s next in .NET!<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>.NET 8 Preview 3 is now available, with  changes to build paths, workloads, Microsoft.Extensions, and containers. It also includes performance improvements in the JIT, for Arm64, and dynamic PGO.<\/p>\n","protected":false},"author":36875,"featured_media":45269,"comment_status":"open","ping_status":"","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[685,7509,756],"tags":[7701],"class_list":["post-45158","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-dotnet","category-aspnetcore","category-csharp","tag-dotnet-8"],"acf":[],"blog_post_summary":"<p>.NET 8 Preview 3 is now available, with  changes to build paths, workloads, Microsoft.Extensions, and containers. It also includes performance improvements in the JIT, for Arm64, and dynamic PGO.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/45158","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/36875"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=45158"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/45158\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/45269"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=45158"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=45158"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=45158"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}