ASP.NET Core updates in .NET 6 Preview 5

Daniel Roth

.NET 6 Preview 5 is now available and includes many great new improvements to ASP.NET Core.

Here’s what’s new in this preview release:

  • .NET Hot Reload updates for dotnet watch
  • ASP.NET Core SPA templates updated to Angular 11 and React 17
  • Use Razor syntax in SVG foreignObject elements
  • Specify null for Action and RenderFragment component parameters
  • Reduced Blazor WebAssembly download size with runtime relinking
  • Configurable buffer threshold before writing to disk in Json.NET output formatter
  • Subcategories for better filtering of Kestrel logs
  • Faster get and set for HTTP headers
  • Configurable unconsumed incoming buffer size for IIS

Get started

To get started with ASP.NET Core in .NET 6 Preview 5, install the .NET 6 SDK.

Note: If you installed .NET 6 Preview 4 previously (either directly or by installing .NET MAUI), then you may run into issues installing and running .NET 6 Preview 5. See the .NET 6 Known Issues for instructions on how to fix up your installation.

If you’re on Windows using Visual Studio, we recommend installing the latest preview of Visual Studio 2019 16.11. Visual Studio 2022 Preview 1 is also releasing today and .NET 6 Preview 5 is included in that release. If you’re on macOS, we recommend installing the latest preview of Visual Studio 2019 for Mac 8.10.

To get setup with .NET MAUI & Blazor for cross-platform native apps, see the latest instructions in the .NET MAUI getting started guide. Be sure to also check out the Announcing .NET MAUI Preview 5 blog post for all the details on what’s new in .NET MAUI in this release.

Note: .NET MAUI is not yet supported in Visual Studio 2022 Preview 1. For .NET MAUI development, please use the latest preview of Visual Studio 2019 16.11 instead.

To install the latest .NET WebAssembly tools for ahead-of-time (AOT) compilation and runtime relinking, run the following command from an elevated command prompt:

dotnet workload install microsoft-net-sdk-blazorwebassembly-aot

If you’ve installed the .NET WebAssembly workload previously, you can update it to .NET 6 Preview 5 by running the following command from an elevated command prompt:

dotnet workload update

Note: There is a known issue with installing optional SDK workloads using the .NET 6 Preview 5 SDK included with Visual Studio 2022 Preview 1. To workaround this issue, install the .NET 6 Preview 5 SDK from https://dot.net/get-dotnet6 after installing Visual Studio 2022 Preview 1.

Upgrade an existing project

To upgrade an existing ASP.NET Core app from .NET 6 Preview 4 to .NET 6 Preview 5:

  • Update all Microsoft.AspNetCore.* package references to 6.0.0-preview.5.*.
  • Update all Microsoft.Extensions.* package references to 6.0.0-preview.5.*.

To upgrade a .NET MAUI Blazor app from .NET 6 Preview 4 to .NET 6 Preview 5 we recommend starting from a new .NET MAUI Blazor project created with the .NET 6 Preview 5 SDK and then copying code over from your original project.

See the full list of breaking changes in ASP.NET Core for .NET 6.

.NET Hot Reload updates for dotnet watch

We’ve been working on various improvements to .NET Hot Reload for .NET 6. Some of these improvements are available in .NET 6 Preview 5, while others are still a work in progress and will be refined in future preview updates.

You no longer need to specify hotReloadProfile in launchSettings.json to use .NET Hot Reload with dotnet watch. .NET Hot Reload with project appropriate behavior is now enabled by default.

When a code edit is made that cannot be hot reloaded (a “rude” edit), dotnet watch will now ask if you want to restart the app to apply the change:

watch : Unable to apply hot reload because of a rude edit. Rebuilding the app...
watch : Unable to handle changes to C:\Users\daroth\Desktop\BlazorApp\Pages\Index.razor.
watch : Do you want to restart your app - Yes (y) / No (n) / Always (a) / Never (v)?

These options have the following behaviors:

  • Choosing Yes will restart the app.
  • Choosing No won’t restart the app and will leave the app running without the changes applied.
  • Choosing Always will restart the app as needed when changes cannot be hot reloaded.
  • Choosing Never won’t restart the app and avoids future prompts.

You can always manually restart the app using Ctrl+R.

Note: There is a known issue in this release that selecting Always still continues to prompt for future rude edits. This will get addressed in a future preview release.

To disable support for .NET Hot Reload when using dotnet watch, use the --no-hot-reload command-line option.

.NET Hot Reload with dotnet watch will also now correctly detect rude edits Blazor WebAssembly apps. Changes applied to Blazor WebAssembly apps will get reapplied to the app when the browser is refreshed or the app is loaded in a separate browser tab or browser instance.

ASP.NET Core SPA templates updated to Angular 11 and React 17

The ASP.NET Core single-page app (SPA) templates for Angular and React have been updated to Angular 11 and React 17. We also expect to further update the Angular template to Angular 12 in a future .NET 6 preview release now that it’s been released.

Support Razor syntax in SVG foreignObject elements

You can now use Razor syntax, including the use of Blazor components, in SVG foreignObject elements:

<svg width="200" height="200" xmlns="http://www.w3.org/2000/svg">
    <rect x="0" y="0" rx="10" ry="10" width="200" height="200" stroke="black" fill="none" />
    <foreignObject x="20" y="20" width="160" height="160">
        <p>@message</p>
    </foreignObject>
</svg>

@code {
    string message = "Wow, it's so nice that this text wraps like it's HTML...because that's what it is!";
}

We also did a bunch of verification and testing to make sure that Blazor has good support for SVG scenarios. We think Blazor’s SVG support is now in good shape. If you hit any issues using SVG in Blazor with this release, please let us know by creating issues on GitHub.

Specify null for Action and RenderFragment component parameters

You can now specify null for the value of Blazor component parameters of type Action and RenderFragment, which simplifies the authoring of components that take optional callback parameters or template parameters.

Reduced Blazor WebAssembly download size with runtime relinking

One of the largest parts of a default Blazor WebAssembly app is the WebAssembly based .NET runtime (dotnet.wasm) that the app carries with it. Blazor WebAssembly already has support for trimming unused code from the .NET core framework libraries. However, the download size of the runtime has been constant.

Not all of the runtime logic is needed by every app. For example, a large part of the runtime logic and related data files are for globalization scenarios. This globalization support enables Blazor WebAssembly apps to handle strings, numbers, dates, etc. based on the current culture. But for apps that don’t need this functionality, all that data and logic is just extra bits.

.NET apps that don’t need globalization functionality can opt out of it and use invariant globalization instead by setting the InvariantGlobalization property to true in their project files. In .NET 5, this would allow a Blazor WebAssembly app to avoid downloading globalization data, but the related logic in the .NET runtime would still be included.

In .NET 6 Preview 5, you can now use the .NET WebAssembly tools (the same tools used for .NET WebAssembly AOT compilation) to relink the runtime to remove unneeded logic and dramatically reduce the size of the runtime. If you have the .NET WebAssembly workload installed, runtime relinking is done automatically when you publish the app. The size reduction is particularly dramatic when using invariant globalization mode.

If you haven’t already, you can install the .NET WebAssembly tools by running the following command from an elevated command prompt:

dotnet workload install microsoft-net-sdk-blazorwebassembly-aot

The following table shows the transfer size of dotnet.wasm for a default Blazor WebAssembly project with .NET 5 and .NET 6:

dotnet.wasm Transfer size (kB)
.NET 5 default 884
.NET 6 default 780
.NET 6 relinked 756
.NET 6 invariant mode 393

Configurable buffer threshold before writing to disk in Json.NET output formatter

The Newtonsoft.Json output formatter by default buffers responses up to 32 KiB in memory before buffering to disk. This is to avoid performing synchronous IO, which can result in other side-effects such as thread starvation and application deadlocks. However, if your response if was larger than 32 KiB this resulted in a lot of avoidable disk I/O. You can now configure the memory threshold before buffering to disk.

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages()
            .AddNewtonsoftJson(options =>
            {
                options.OutputFormatterMemoryBufferThreshold = 48 * 1024;
            });
}

Note: We still recommend using the System.Text.Json output formatter unless you require the Newtonsoft.Json serializer for compatibility reasons. The System.Text.Json serializer is fully async and will work efficiently for any size payload.

Subcategories for better filtering of Kestrel logs

Prior to this change, enabling verbose logging for Kestrel was prohibitively expensive as all of Kestrel shared the same logging category name (Microsoft.AspNetCore.Server.Kestrel). We’ve now split up that category into multiple new subcategories:

  • Microsoft.AspNetCore.Server.Kestrel (current category): ApplicationError, ConnectionHeadResponseBodyWrite, ApplicationNeverCompleted, RequestBodyStart, RequestBodyDone, RequestBodyNotEntirelyRead, RequestBodyDrainTimedOut, ResponseMinimumDataRateNotSatisfied, InvalidResponseHeaderRemoved, HeartbeatSlow.
  • Microsoft.AspNetCore.Server.Kestrel.BadRequests: ConnectionBadRequest, RequestProcessingError, RequestBodyMinimumDataRateNotSatisfied.
  • Microsoft.AspNetCore.Server.Kestrel.Connections: ConnectionAccepted, ConnectionStart, ConnectionStop, ConnectionPause, ConnectionResume, ConnectionKeepAlive, ConnectionRejected, ConnectionDisconnect, NotAllConnectionsClosedGracefully, NotAllConnectionsAborted, ApplicationAbortedConnection.
  • Microsoft.AspNetCore.Server.Kestrel.Http2: Http2ConnectionError, Http2ConnectionClosing, Http2ConnectionClosed, Http2StreamError, Http2StreamResetAbort, HPackDecodingError, HPackEncodingError, Http2FrameReceived, Http2FrameSending, Http2MaxConcurrentStreamsReached.
  • Microsoft.AspNetCore.Server.Kestrel.Http3: Http3ConnectionError, Http3ConnectionClosing, Http3ConnectionClosed, Http3StreamAbort, Http3FrameReceived, Http3FrameSending.

While your existing rules will continue to work (log filtering applies rules with the longest matching category prefix), you can now be more selective on which rules you enable. For example, the observability overhead of enabling Debug logging for just bad requests is greatly reduced and can be achieved with the following configuration:

{
  "Logging": {
    "LogLevel": {
      "Microsoft.AspNetCore.Kestrel.BadRequests": "Debug"
    }
  }
}

Faster get and set for HTTP headers

We added new API to expose all common headers available on System.Net.Http.HeaderNames as properties on the Microsoft.AspNetCore.Http.IHeaderDictionary resulting in an easier to use API. For example, the in-line middleware below gets/sets both request and response headers using the new APIs:

app.Use(async (context, next) =>
{
    var hostHeader = context.Request.Headers.Host;
    app.Logger.LogInformation("Host header: {host}", hostHeader);
    context.Response.Headers.XPoweredBy = "ASP.NET Core 6.0-preview5";
    await next.Invoke(context);
    var dateHeader = context.Response.Headers.Date;
    app.Logger.LogInformation("Response date: {date}", dateHeader);
});

For implemented headers the get/set accessors are implemented by going directly to the field and bypassing the lookup. For non-implemented headers, the accessors can bypass the initial lookup against implemented headers and directly perform the Dictionary<string, StringValues> lookup. This results in faster access for both scenarios.

Method Branch Type Mean Op/s Delta
GetHeaders preview4 Plaintext 25.793 ns 38,770,569.6
GetHeaders preview5 Plaintext 12.775 ns 78,279,480.0 +101.9%
GetHeaders preview4 Common 121.355 ns 8,240,299.3
GetHeaders preview5 Common 37.598 ns 26,597,474.6 +222.8%
GetHeaders preview4 Unknown 366.456 ns 2,728,840.7
GetHeaders preview5 Unknown 223.472 ns 4,474,824.0 +64.0%
SetHeaders preview4 Plaintext 49.324 ns 20,273,931.8
SetHeaders preview5 Plaintext 34.996 ns 28,574,778.8 +40.9%
SetHeaders preview4 Common 635.060 ns 1,574,654.3
SetHeaders preview5 Common 108.041 ns 9,255,723.7 +487.7%
SetHeaders preview4 Unknown 1,439.945 ns 694,470.8
SetHeaders preview5 Unknown 517.067 ns 1,933,985.7 +178.4%

Configurable unconsumed incoming buffer size for IIS

Prior to this change, the IIS server only buffered 64 KiB of unconsumed request bodies. This resulted in reads being constrained to that maximum size, which impacts the performance when large incoming bodies such as large uploads. In .NET 6 Preview 5, we’ve changed the default buffer size from 64 KiB to 1 MiB which should result in improved throughput for large uploads. In our tests, a 700 MiB upload that used to take 9 seconds now only takes 2.5 seconds.

The downside of a larger buffer size is an increased per-request memory consumption when the app isn’t quickly reading from the request body. So, in addition to changing the default buffer size, we’ve also made the buffer size configurable, allowing you to tune it based on your workload.

public void ConfigureServices(IServiceCollection services)
{
    services.Configure<IISServerOptions>(
        options =>
        {
            options.MaxRequestBodySize = 64 * 1024;
        }
    );
}

Give feedback

We hope you enjoy this preview release of ASP.NET Core in .NET 6. We’re eager to hear about your experiences with this release. Let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core!

53 comments

Discussion is closed. Login to edit/delete existing comments.

  • Stevie White 0

    We started using Blazor for a couple of projects at my company this year and I can say that it has been a tremendous help for all of us. Coming from a Vue/Angular/Xaml/MVC background, it felt super intuitive. Personally, I have been following the project since Steve first started talking about Blazor, which now feels like last week (man time has flown)!

    Anyways, I’m super happy with the speed that Blazor has developed, and I am looking forward to the new feature with 6 (in particularly the new options for error handling and hot reload)!

    • Daniel RothMicrosoft employee 0

      Thanks Stephen! It’s great to hear that Blazor is working out for you!

  • Stk 0

    I just wanna thank the whole Blazor team for the amazing work you are doing.

    We have already ported 5 React + . NET api solutions and the development speed is amazing while having a blast.

    Blazor is for sure the future. Rock on!

    • Daniel RothMicrosoft employee 0

      Awesome! That’s great to hear!

  • piercarlo schiavo 0

    Hello,
    after upgrade the preview 4 to 5 I have following beared authentication problem:

    System.ArgumentOutOfRangeException: The UTC time represented when the offset is applied must be between year 0 and 10,000. (Parameter 'offset')
       at System.DateTimeOffset.ValidateDate(DateTime dateTime, TimeSpan offset) in System.Private.CoreLib.dll:token 0x6000dd9+0x0
       at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync() in Microsoft.AspNetCore.Authentication.JwtBearer.dll:token 0x6000032+0x0
    Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler: Error: Exception occurred while processing message.

    I’m using WebAssembly Hosted blazor App, where I applied authentication:

                services.AddAuthentication(autOptions =>
                {
                    autOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
                    autOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
                    autOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                })
                .AddJwtBearer(options =>
                {
                    options.SaveToken = true;
                    options.TokenValidationParameters = new TokenValidationParameters
                    { 
                        ValidateIssuerSigningKey = true,
                        ValidateIssuer = true,
                        ValidateAudience = true,
                        RequireExpirationTime = true,
                        ValidateLifetime = true,
                        ValidIssuer = Configuration["GlobalSettings:JwtSettings:JwtIssuer"],
                        ValidAudience = Configuration["GlobalSettings:JwtSettings:JwtAudience"],
                        IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["GlobalSettings:JwtSettings:JwtSecurityKey"]))
                    };
                });

    Thankyou for tips

    Piercarlo

    • piercarlo schiavo 0

      Ok,
      Installed the new “preview 6” and error was resolved.

      Piercarlo

  • Rui Lima 0

    Blazor AOT is generating a huge dotnet.wasm (106,640K) vs the non AOT of (2,172K), this is a big killer. I must be missing something.

    • Daniel RothMicrosoft employee 0

      Hi Rui. That is surprisingly large! What is the total size of the .NET assemblies downloaded by your app when you don’t use AOT compilation?

      • Rui Lima 0

        Hi Daniel, with dotnet 5: 13.02 MB / 3.99 MB transferred
        I haven’t deployed yet with dotnet 6.

        Doing the publish locally from VS2019 latest preview
        the wwwroot/_framework has:
        AOT: 174 MB
        non AOT: 33.0 MB

        pic with file properties window:
        https://ibb.co/Sc8MftY

        global.json:
        {
        “sdk”: {
        “version”: “6.0.100-preview.5.21302.13”
        }
        }

          • Rui Lima 0

            Over the weekend I’ll try to see if I can create a sample project that present the same symptoms.
            Unfortunately, I cannot share the project where I am seeing this. If I manage to recreate the issue I will open the new github issue and add the sample project there.

  • Rod Macdonald 0

    Hi Daniel, with the new Windows 11 store on the horizon, can I please ask what the deployment scenario(s) will be for Blazor moving forwards and whether Blazor’s PWA capabilities will match those of the announced PWAbuilder. Thank you.

  • dwkrp 0

    Thank you for the great MAUI and Blazor work. So awesome! I am curious if there is any way to also host Kestrel in a MAUI Blazor app so we could include some HTTP Controllers in the same process?

    • Daniel RothMicrosoft employee 0

      Yup, hosting ASP.NET Core & Kestrel in a .NET MAUI process should be fine. If you run into any issues with it, let us know!

      • dwkrp 0

        Thank you Daniel. Should I expect the following to work? (i.e. adding ConfigureWebHostDefaults into the MAUI startup class)

        public void Configure(IAppHostBuilder appBuilder)
        {
            appBuilder
                .RegisterBlazorMauiWebView(typeof(Startup).Assembly)
                .UseMicrosoftExtensionsServiceProviderFactory()
                .UseMauiApp()
                .ConfigureFonts(fonts =>
                {
                        fonts.AddFont("OpenSans-Regular.ttf", "OpenSansRegular");
                })
                .ConfigureWebHostDefaults(webBuilder =>
                {
                        webBuilder.UseStartup();
                })
                .ConfigureServices(services =>
                {
                        services.AddBlazorWebView();
                        services.AddSingleton();
                })
                ;
        }
        

        At runtime I get an exception that the ‘configuration’ parameter cannot be null…

        at Microsoft.AspNetCore.Hosting.WebHostOptions..ctor(IConfiguration configuration, String applicationNameFallback)
        at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.GetWebHostBuilderContext(HostBuilderContext context)
        at Microsoft.AspNetCore.Hosting.GenericWebHostBuilder.<.ctor>b__6_1(HostBuilderContext context, IConfigurationBuilder configurationBuilder)
        at Microsoft.Maui.Hosting.AppHostBuilder.BuildAppConfiguration()
        at Microsoft.Maui.Hosting.AppHostBuilder.Build()
        at Microsoft.Maui.MauiWinUIApplication.OnLaunched(LaunchActivatedEventArgs args)
        at maui.blazor.WinUI.App.OnLaunched(LaunchActivatedEventArgs args) in ~project-path~\Platforms\Windows\App.xaml.cs:line 29
        at Microsoft.UI.Xaml.Application.Microsoft.UI.Xaml.IApplicationOverrides.OnLaunched(LaunchActivatedEventArgs args)
        at ABI.Microsoft.UI.Xaml.IApplicationOverrides.Do_Abi_OnLaunched_0(IntPtr thisPtr, IntPtr args)

        BTW, I was using .NET 6 Preview 6 for this

        • Daniel RothMicrosoft employee 0

          Looks like .NET MAUI doesn’t provide an IConfiguration service, so you’ll need to add that. Also, while this should be fine on desktop I don’t know that you’ll be able to get ASP.NET Core to work on iOS or Android.

Feedback usabilla icon