ASP.NET Core updates in .NET 8 Preview 3

Daniel Roth

.NET 8 Preview 3 is now available and includes many great new improvements to ASP.NET Core.

Here’s a summary of what’s new in this preview release:

  • ASP.NET Core support for native AOT
  • Server-side rendering with Blazor
  • Render Razor components outside of ASP.NET Core
  • Sections support in Blazor
  • Monitor Blazor Server circuit activity
  • SIMD enabled by default for Blazor WebAssembly apps
  • Request timeouts
  • Short circuit routes

For more details on the ASP.NET Core work planned for .NET 8 see the full ASP.NET Core roadmap for .NET 8 on GitHub.

Get started

To get started with ASP.NET Core in .NET 8 Preview 3, install the .NET 8 SDK.

If you’re on Windows using Visual Studio, we recommend installing the latest Visual Studio 2022 preview. Visual Studio for Mac support for .NET 8 previews isn’t available at this time.

Upgrade an existing project

To upgrade an existing ASP.NET Core app from .NET 8 Preview 2 to .NET 8 Preview 3:

  • Update the target framework of your app to net8.0.
  • Update all Microsoft.AspNetCore.* package references to 8.0.0-preview.3.*.
  • Update all Microsoft.Extensions.* package references to 8.0.0-preview.3.*.

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

ASP.NET Core support for native AOT

In .NET 8 Preview 3, we’re very happy to introduce native AOT support for ASP.NET Core, with an initial focus on cloud-native API applications. It’s now possible to publish an ASP.NET Core app with native AOT, producing a self-contained app that’s ahead-of-time (AOT) compiled to native code.

Native AOT apps can have a smaller deployment size, start up very quickly, and use less memory. The application can be run on a machine that doesn’t have the .NET runtime installed. The benefit of native AOT is most significant for workloads with many deployed instances, such as cloud infrastructure and hyper-scale services.

Benefits of using native AOT with ASP.NET Core

Publishing and deploying a native AOT app can provide the following benefits:

  • Reduced disk footprint: When publishing using native AOT, a single executable is produced containing the program along with the subset of code from external dependencies that the program uses. Reduced executable size can lead to:
    • Smaller container images, for example in containerized deployment scenarios.
    • Reduced deployment time due to smaller images.
  • Reduced startup time: Native AOT applications can show reduced start-up times, in part due to the removal of JIT compilation. Reduced start-up means:
    • The app is ready to service requests quicker.
    • Improved deployment with container orchestrators that manage transitions from one version of the app to another.
  • Reduced memory demand: Native AOT apps can have reduced memory demands depending on the work being performed by the app. Reduced memory consumption can lead to greater deployment density and improved scalability.

As an example, we ran a simple ASP.NET Core API app in our benchmarking lab to compare the differences in app size, memory use, startup time, and CPU load, published with and without native AOT:

Publish kind Startup time (ms) App size (MB)
Default 169 88.5
Native AOT 34 11.3

The table above shows that publishing the app as native AOT dramatically improves startup time and app size. Startup time was reduced by 80%! And app size was reduced by 87%!

You can explore these and more metrics on our public benchmarks dashboard.

ASP.NET Core and native AOT compatibility

Not all features in ASP.NET Core are compatible with native AOT. Similarly, not all libraries commonly used in ASP.NET Core are compatible with native AOT. .NET 8 represents the start of work to enable native AOT in ASP.NET Core, with an initial focus on enabling support for apps using Minimal APIs or gRPC, and deployed in cloud-native environments. Your feedback will help guide our efforts during .NET 8 previews and beyond, to ensure we focus on the places where the benefits of native AOT can have the largest impact.

Native AOT applications come with a few fundamental compatibility requirements. The key ones include:

  • No dynamic loading (for example, Assembly.LoadFile)
  • No runtime code generation via JIT (for example, System.Reflection.Emit)
  • No C++/CLI
  • No built-in COM (only applies to Windows)
  • Requires trimming, which has limitations
  • Implies compilation into a single file, which has known incompatibilities
  • Apps include required runtime libraries (just like self-contained apps, increasing their size as compared to framework-dependent apps)

Reflection is a commonly used feature of .NET, allowing for runtime discovery and inspection of type information, dynamic invocation, and code generation. While reflection over type information is supported in native AOT, the trimming performed often can’t statically determine that a type has members that are only accessed via reflection, and are thus removed, leading to runtime exceptions. Developers can annotate their code with instructional attributes, such as [DynamicallyAccessedMembers] to indicate that members are dynamically accessed and should be left untrimmed.

Developers should also be aware that it’s not always immediately obvious that code is relying on reflection’s runtime code generation capabilities and thus can introduce native AOT incompatibilties, e.g. methods like Type.MakeGenericType rely on capabilities not available in native AOT. Roslyn source generators allow code to be generated at compile time with similar type discovery and inspection capabilities as runtime-based reflection, and are a useful alternative when preparing for native AOT compatibility.

The following table summarizes ASP.NET Core feature compatibility with native AOT:

Feature Fully Supported Partially Supported Not Supported
gRPC Fully supported
Minimal APIs Partially supported
MVC Not supported
Blazor Not supported
SignalR Not supported
Authentication Not supported (JWT soon)
CORS Fully supported
Health checks Fully supported
Http logging Fully supported
Localization Fully supported
Output caching Fully supported
Rate limiting Fully supported
Request decompression Fully supported
Response caching Fully supported
Response compression Fully supported
Rewrite Fully supported
Session Not supported
SPA Not supported
Static files Fully supported
WebSockets Fully supported

During .NET 8, you can keep track of current known issues regarding ASP.NET Core and native AOT compatibility here.

It is important to test your application thoroughly when moving to a native AOT deployment model to ensure that functionality observed during development (when the app is untrimmed and JIT-compiled) is preserved in the native executable. When building your application, keep an eye out for AOT warnings. An application that produces AOT warnings during publishing is not guaranteed to work correctly. If you don’t get any AOT warnings at publish time, you should be confident that your application will work consistently after publishing for AOT as it did during your F5 / dotnet run development workflow.

Minimal APIs and native AOT

In order to make Minimal APIs compatible with native AOT, we’re introducing the Request Delegate Generator (RDG). The RDG is a source generator that performs similar work to the RequestDelegateFactory (RDF), turning the various MapGet(), MapPost(), etc., calls in your application into RequestDelegates associated with the specified routes, but rather than doing it in-memory in your application when it starts, it does it at compile-time and generates C# code directly into your project. This removes the runtime generation of this code, and ensures the types used in your APIs are rooted in your application code in a way that is statically analyzable by the native AOT tool-chain, ensuring that required code is not trimmed away. We’re working to ensure that as many as possible of the Minimal API features you enjoy today are supported by the RDG and thus compatible with native AOT.

The RDG is enabled automatically in your project when you enable publishing with native AOT. You can also manually enable RDG even when not using native AOT by setting <EnableRequestDelegateGenerator>true</EnableRequestDelegateGenerator> in your project file. This can be useful when initially evaluating your project’s readiness for native AOT, or potentially to reduce the startup time of your application.

Minimal APIs is optimized for receiving and returning JSON payloads using System.Text.Json, and as such the compatibility requirements for JSON and native AOT apply too. This requires the use of the System.Text.Json source generator. All types accepted as parameters to or returned from request delegates in your Minimal APIs must be configured on a JsonSerializerContext that is registered via ASP.NET Core’s dependency injection, e.g.:

// Register the JSON serializer context with DI
builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.AddContext<AppJsonSerializerContext>();
});

...

// Add types used in your Minimal APIs to source generated JSON serializer content
[JsonSerializable(typeof(Todo[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{

}

gRPC and native AOT

gRPC supports native AOT in .NET 8. Native AOT enables publishing gRPC client and server apps as small, fast native executables. Learn more about gRPC and native AOT in the related docs.

Libraries and native AOT

Many of the common libraries you enjoy using in your ASP.NET Core projects today will have some compatibility issues when used in a project targeting native AOT. Popular libraries often rely on the dynamic capabilities of .NET reflection to inspect and discover types, conditionally load libraries at runtime, and generate code on the fly to implement their functionality. As stated earlier, these behaviors can cause compatibility issues with native AOT, and as such, these libraries will need to be updated in order to work with native AOT by using tools like Roslyn source generators.

Library authors wishing to learn more about preparing their libraries for native AOT are encouraged to start by preparing their library for trimming and learning more about the native AOT compatibility requirements.

Getting started with native AOT deployment in ASP.NET Core

Native AOT is a publishing option. AOT compilation happens when the app is published. A project that uses Native AOT publishing will use JIT compilation when debugging or running as part of the developer inner-loop, but there are some observable differences:

  • Some features that aren’t compatible with native AOT are disabled and throw exceptions at runtime.
  • A source analyzer is enabled to highlight project code that isn’t compatible with Native AOT. At publish time, the entire app, including NuGet packages, is analyzed for compatibility again.

Native AOT analysis during publish includes all code from the app and the libraries the app depends on. Review native AOT warnings and take corrective steps. It’s a good idea to test publishing apps frequently to discover issues early in the development lifecycle.

The following prerequisites need to be installed before publishing .NET projects with native AOT.

On Windows, install Visual Studio 2022, including Desktop development with C++ workload with all default components.

On Linux, install the compiler toolchain and developer packages for libraries that the .NET runtime depends on.

  • Ubuntu (18.04+)
    sudo apt-get install clang zlib1g-dev
  • Alpine (3.15+)
    sudo apk add clang build-base zlib-dev

Note that at this time, cross-platform native AOT publishing is not supported, meaning you need to perform the publish on the same platform type as the intended target, e.g. if targeting a deployment to Linux, perform the publish on Linux.

Native AOT published ASP.NET Core apps can be deployed and run anywhere native executables can. Containers are a popular choice for this. Native AOT published .NET applications have the same platform requirements as .NET self-contained applications, and as such should set mcr.microsoft.com/dotnet/runtime-deps as their base image.

Native AOT-ready project templates

Options for the new ASP.NET Core API project template in Visual Studio

In this preview, there are two native AOT-enabled project templates to help get you started trying out ASP.NET Core with native AOT.

The “ASP.NET Core gRPC Service” project template has been updated to include a new “Enable native AOT publish” option that, when selected, configures the new project to publish as native AOT. This is done by setting <PublishAot>true</PublishAot> in the project’s .csproj file.

There is also a brand new “ASP.NET Core API” project template, that produces a project more directly focused on cloud-native, API-first scenarios. This also has the “Enable native AOT publish” option, and differs from the existing “Web API” project template in the following ways:

  • Uses Minimal APIs only, as MVC is not yet native AOT compatible
  • Uses the new WebApplication.CreateSlimBuilder API to ensure only the essential features are enabled by default, minimzing the app’s deployed size
  • Configured to listen on HTTP only, as HTTPS traffic is commonly handled by an ingress service in cloud-native deployments
  • Does not include a launch profile for running under IIS or IIS Express
  • Enables the JSON serializer source generator when “Enable native AOT publish” is specified during project creation
  • Includes a sample “Todos API” instead of the weather forecast sample
  • Configured to use Workstation GC in order to minimize memory use. Note this aspect is temporary as we work on GC improvements in .NET 8 intended to provide more dynamic scaling of memory use based on application load. Learn more about memory use and GC in ASP.NET Core apps here.

You can create a new API project configured to publish as native AOT using the dotnet CLI:

$ dotnet new api -aot

Here is the content of Program.cs in a project created with the new “ASP.NET Core API” template:

using System.Text.Json.Serialization;
using MyApiApplication;

var builder = WebApplication.CreateSlimBuilder(args);
builder.Logging.AddConsole();

builder.Services.ConfigureHttpJsonOptions(options =>
{
    options.SerializerOptions.AddContext<AppJsonSerializerContext>();
});

var app = builder.Build();

var sampleTodos = TodoGenerator.GenerateTodos().ToArray();

var todosApi = app.MapGroup("/todos");
todosApi.MapGet("/", () => sampleTodos);
todosApi.MapGet("/{id}", (int id) =>
    sampleTodos.FirstOrDefault(a => a.Id == id) is { } todo
        ? Results.Ok(todo)
        : Results.NotFound());

app.Run();

[JsonSerializable(typeof(Todo[]))]
internal partial class AppJsonSerializerContext : JsonSerializerContext
{

}

Native AOT call to action

Native AOT deployment is not suitable for every application, but in the scenarios where the benefits that native AOT offers are compelling, it can have a huge impact. While it’s still early, we’d love for you try out native AOT support for ASP.NET Core in .NET 8 Preview 3, and share any feedback you have by leaving a comment here, or logging an issue on GitHub. Be sure to read the current known issues regarding ASP.NET Core and native AOT compatibility.

In future previews, we’re working to enable more features of ASP.NET Core and supporting technologies with native AOT, including JWT authentication, options validation, ADO.NET data access for SQLite and PostgreSQL, and OpenTelemetry. We’re also working on improving the Visual Studio experience for configuring and working on ASP.NET Core projects targeting native AOT.

Server-side rendering with Blazor components

This preview adds initial support for server-side rendering with Blazor components. This is the beginnings of the Blazor unification effort to enable using Blazor components for all your web UI needs, client-side and server-side. This is an early preview of the functionality, so it’s still somewhat limited, but our goal is to enable using reusable Blazor components no matter how you choose to architect your app.

Server-side rendering (SSR) is when the server generates HTML in response to a request. Apps that use SSR load fast because all of the hard work of rendering the UI is being done on the server without the need to download a large JavaScript bundle. ASP.NET Core has existing support for SSR with MVC and Razor Pages, but these frameworks lack a component model for building reusable pieces of web UI. That’s where Blazor comes in! We’re adding support for building server-rendered UI using Blazor components that can then also be extended to the client to enable rich interactivity.

In this preview you can use Blazor components to do server-side rendering without the need for any .cshtml files. The framework will discover routable Blazor components and set them up as endpoints. There’s no WebAssembly or WebSocket connections involved. You don’t need to load any JavaScript. Each request is handled independently by the Blazor component for the corresponding endpoint.

To try out server-side rendering with Blazor:

  1. Create a new empty ASP.NET Core web app:

    dotnet new web -o WebApp1
    cd WebApp1
  2. Add a simple Razor component to the project:

    dotnet new razorcomponent -n MyComponent
  3. Update MyComponent.razor to make it a proper HTML page with a route:

    @page "/"
    @implements IRazorComponentApplication<MyComponent>
    
    <!DOCTYPE html>
    <html lang="en">
    <body>
        <h1>Hello Blazor!</h1>
        <p>The time is @DateTime.Now.ToShortTimeString()</p>
    </body>
    </html>

    You’ll also need to implement the IRazorComponentApplication interface on this component, which is currently used to aid in discovering component endpoints within the app. This design is likely to change in a later update, but for now this interface is required.

  4. In Program.cs setup the Razor component services by calling AddRazorComponents().

    builder.Services.AddRazorComponents();
  5. Map endpoints for you components by calling MapRazorComponents<TComponent>(). You’ll need to add a using directive for your component:

    @using WebApp1
    ...
    app.MapRazorComponents<MyComponent>();

    Routable components will be automatically discovered within the assembly MyComponent resides in. Again, note that TComponent must currently implement IRazorComponentApplication, but this design is likely to change in a later update.

  6. Run the app and browse to the app root to see your component rendered:

    Blazor server-side rendering

If you take a look at what’s happening on the network in the browser dev tools, you’ll notice that you don’t see any WebSocket connections or WebAssembly being downloaded. It’s just a single request returning fully rendered HTML from the server. This also means there isn’t any support for interactivity yet. For example, if you add a button with an @onclick handler it won’t do anything when clicked because there’s nothing setup to execute the handler. Integration with client interactivity using Blazor Server or Blazor WebAssembly is forthcoming.

All of the routing to the Blazor component endpoints is being done with ASP.NET Core endpoint routing. The Blazor router currently isn’t involved at all. This means that if you want to use a layout then you’ll need to manually apply it using the the @layout directive. A convenient way to do this is using a _Imports.razor file, which will apply directives to an entire folder . We’re looking at integrating the Blazor router with endpoint routing to make this experience a bit more seamless.

We’ve also added RazorComponentResult and RazorComponentResult<T> types that implement IResult for rendering Razor components. These result types can be returned from a manually configured endpoint:

app.MapGet("/my-component", () => new RazorComponentResult<MyComponent>());

You can also return them from an MVC controller action if you want to leverage Blazor components from an existing MVC app:

public class HomeController : Controller
{
    public IResult MyComponent()
    {
        return new RazorComponentResult<MyComponent>();
    }
}

You can find a complete sample of the default Blazor project template implemented purely with server-side rendering (but without client-side interactivity) in the ASP.NET Core GitHub repo.

This is just the beginnings of server-side rendering support with Blazor in .NET 8. You can expect plenty of improvements and enhancements in upcoming preview releases. Stay tuned!

Render Razor components outside of ASP.NET Core

A convenient side-effect of the work to enable server-side rendering with Blazor components is that you can now render Blazor components outside the context of an HTTP request. You can render Razor components as HTML directly to a string or stream independently of the ASP.NET Core hosting environment. This is convenient for scenarios where you want to generate HTML fragments, like for a generated email, or even for generating static site content.

For example, here’s how you render a Razor component to an HTML string from a console app:

  1. Create a new console app project:

    dotnet new console -o ConsoleApp1
    cd ConsoleApp1
  2. Add package references to Microsoft.AspNetCore.Components.Web and Microsoft.Extensions.Logging:

    dotnet add package Microsoft.AspNetCore.Components.Web --prerelease
    dotnet add package Microsoft.Extensions.Logging --prerelease
  3. Update your console app project to use the Razor SDK:

    <Project Sdk="Microsoft.NET.Sdk.Razor">
    ...
    </Project>
  4. Add a Razor component to the project that you want to render:

    dotnet new razorcomponent -n MyComponent
  5. Update Program.cs to create an HtmlRender and render your component by calling RenderComponentAsync. You’ll need to setup dependency injection and logging first. Also note that any calls to RenderComponentAsync must be made in the context of calling InvokeAsync on a component dispatcher. A component dispatcher is readily available from the HttmlRender.Dispatcher property.

    using Microsoft.AspNetCore.Components;
    using Microsoft.AspNetCore.Components.Web;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Logging;
    using ConsoleApp1
    
    IServiceCollection services = new ServiceCollection();
    services.AddLogging();
    
    IServiceProvider serviceProvider = services.BuildServiceProvider();
    ILoggerFactory loggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
    
    await using var htmlRenderer = new HtmlRenderer(serviceProvider, loggerFactory);
    var html = await htmlRenderer.Dispatcher.InvokeAsync(async () =>
    {
        var parameters = ParameterView.Empty;
        var output = await htmlRenderer.RenderComponentAsync<MyComponent>(parameters);
        return output.ToHtmlString();
    });
    
    Console.WriteLine(html);

    Alternatively, you can write the HTML to a TextWriter by calling output.WriteHtmlTo(textWriter).

The task returned by RenderComponentAsync will complete when the component has fully rendered, including completing any async lifecycle methods. If you want to observe the rendered HTML earlier you can call BeginRenderComponentAsync instead. You can then wait for the component rendering to complete by awaiting WaitForQuiescenceAsync on the returned HtmlComponent instance.

Sections support in Blazor

The new SectionOutlet and SectionContent components in Blazor add support for specifying outlets for content that can be filled in later. Sections are often used to define placeholders in layouts that are then filled in by specific pages. Sections are referenced either by a unique name, or using a unique object ID.

For example, try adding a SectionOutlet to the top row of the MainLayout component in the default Blazor template. You’ll need to first add a using directive for Microsoft.AspNetCore.Components.Sections, which is easiest to do in the root _Imports.razor file:

@using Microsoft.AspNetCore.Components.Sections
...
<div class="top-row px-4">
    <SectionOutlet SectionName="TopRowSection" />
    <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
</div>

Pages can then specify what content to display in the TopRowSection using the SectionContent component. For example, in Counter.razor you might add another button to the top row that can increment the counter, like this:

<SectionContent SectionName="TopRowSection">
    <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
</SectionContent>

Monitor Blazor Server circuit activity

You can now monitor inbound circuit activity in Blazor Server apps using the new CreateInboundActivityHandler method on CircuitHandler. Inbound circuit activity is any activity sent from the browser to the server, like UI events or JavaScript-to-.NET interop calls.

For example, you can use a circuit activity handler to detect if the client is idle, like this:

public sealed class IdleCircuitHandler : CircuitHandler, IDisposable
{
    readonly Timer timer;
    readonly ILogger logger;

    public IdleCircuitHandler(IOptions<IdleCircuitOptions> options, ILogger<IdleCircuitHandler> logger)
    {
        timer = new Timer();
        timer.Interval = options.Value.IdleTimeout.TotalMilliseconds;
        timer.AutoReset = false;
        timer.Elapsed += CircuitIdle;
        this.logger = logger;
    }

    private void CircuitIdle(object? sender, System.Timers.ElapsedEventArgs e)
    {
        logger.LogInformation(nameof(CircuitIdle));
    }

    public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(Func<CircuitInboundActivityContext, Task> next)
    {
        return context =>
        {
            timer.Stop();
            timer.Start();
            return next(context);
        };
    }

    public void Dispose()
    {
        timer.Dispose();
    }
}

public class IdleCircuitOptions
{
    public TimeSpan IdleTimeout { get; set; } = TimeSpan.FromMinutes(5);
}

public static class IdleCircuitHandlerServiceCollectionExtensions
{
    public static IServiceCollection AddIdleCircuitHandler(this IServiceCollection services, Action<IdleCircuitOptions> configureOptions)
    {
        services.Configure(configureOptions);
        services.AddIdleCircuitHandler();
        return services;
    }

    public static IServiceCollection AddIdleCircuitHandler(this IServiceCollection services)
    {
        services.AddScoped<CircuitHandler, IdleCircuitHandler>();
        return services;
    }
}

In a future update we plan to add APIs for evicting idle circuits so they no longer consume server resources unnecessarily.

Circuit activity handlers also provide a way to access scoped Blazor services from other non-Blazor dependency injection (DI) scopes, like scopes created using IHttpClientFactory. There is an existing pattern for accessing circuit scoped services from other DI scopes, but it requires using a custom base component type. With circuit activity handlers we can use a better approach:

public class CircuitServicesAccessor
{
    static readonly AsyncLocal<IServiceProvider> blazorServices = new();

    public IServiceProvider? Services
    {
        get => blazorServices.Value;
        set => blazorServices.Value = value;
    }
}

public class ServicesAccessorCircuitHandler : CircuitHandler
{
    readonly IServiceProvider services;
    readonly CircuitServicesAccessor circuitServicesAccessor;

    public ServicesAccessorCircuitHandler(IServiceProvider services, CircuitServicesAccessor servicesAccessor)
    {
        this.services = services;
        this.circuitServicesAccessor = servicesAccessor;
    }

    public override Func<CircuitInboundActivityContext, Task> CreateInboundActivityHandler(Func<CircuitInboundActivityContext, Task> next)
    {
        return async context =>
        {
            circuitServicesAccessor.Services = services;
            await next(context);
            circuitServicesAccessor.Services = null;
        };
    }
}

public static class CircuitServicesServiceCollectionExtensions
{
    public static IServiceCollection AddCircuitServicesAccessor(this IServiceCollection services)
    {
        services.AddScoped<CircuitServicesAccessor>();
        services.AddScoped<CircuitHandler, ServicesAccessorCircuitHandler>();
        return services;
    }
}

You can then access the circuit scoped services by injecting the CircuitServicesAccessor where it’s needed. For example, here’s how you can access the AuthenticationStateProvider from a DelegatingHandler setup using IHttpClientFactory:

public class AuthenticationStateHandler : DelegatingHandler
{
    readonly CircuitServicesAccessor circuitServicesAccessor;

    public AuthenticationStateHandler(CircuitServicesAccessor circuitServicesAccessor)
    {
        this.circuitServicesAccessor = circuitServicesAccessor;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        var authStateProvider = circuitServicesAccessor.Services.GetRequiredService<AuthenticationStateProvider>();
        var authState = await authStateProvider.GetAuthenticationStateAsync();

        // ...

        return await base.SendAsync(request, cancellationToken);
    }
}

SIMD enabled by default for Blazor WebAssembly apps

WebAssembly fixed-width SIMD (Single Instruction, Multiple Data) support is now available with all major browsers and we’ve enabled it by default for ahead-of-time (AOT) compiled Blazor WebAssembly apps. SIMD can improve the throughput of vectorized computations by performing an operation on multiple pieces of data, in parallel, using a single instruction.

Request timeouts

In this preview release, we’ve also added a new middleware for supporting request timeouts. You can set request timeouts for individual endpoints, controllers, or dynamically per request.

To apply request timeouts, first add the request timeout services:

builder.Services.AddRequestTimeouts();

You can then apply request timeouts using middleware by calling UseRequestTimeouts():

app.UseRequestTimeouts();

To apply request timeouts to a particular endpoint call WithRequestTimeout(timeout) or add [RequestTimeout(timeout)] to the controller or action.

The timeouts are cooperative; when they expire, the HttpContext.RequestAborted cancellation token will trigger but the request will not be forcibly aborted. It’s up to the application to monitor the token and decide how to end the request processing.

Thank you @Kahbazi for contributing this feature!

Short circuit routes

When routing matches an endpoint it typically lets the rest of the middleware pipeline run before invoking the endpoint logic. If that’s not necessary or desirable, there’s a new option that will cause routing to invoke the endpoint logic immediately and then end the request. This can be used to efficiently respond to requests that don’t require additional features like authentication, CORS, etc., such as requests for robots.txt or favicon.ico.

app.MapGet("/", () => "Hello World").ShortCircuit();

You can also use the MapShortCircuit method to setup endpoints that send a quick 404 or other status code for matched resources that you don’t want to further process.

app.MapShortCircuit(404, "robots.txt", "favicon.ico");

This was another great feature contribution from @Kahbazi. Thank you!

Give feedback

We hope you enjoy this preview release of ASP.NET Core in .NET 8. Let us know what you think about these new improvements by filing issues on GitHub.

Thanks for trying out ASP.NET Core!

33 comments

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

  • Ömer KAYA 1

    Hi, I have 2 questions.

    1) Will Blazor United be available in .Net 8 and will Blazor WASM continue after Blazor United?

    2) The most important feedback we get from our customers is that Blazor WASM opens very slowly and the main reason for this is the size of the downloaded files. In some projects, the downloaded files are 20 MB in size, but the boot time can be up to 45 seconds. However, downloading a 20 MB file takes 5-10 seconds on my internet. Blazor, on the other hand, takes longer because it downloads these files sequentially. Can’t we download all files in parallel? I know Blazor downloads and runs sequentially, but can’t it trigger itself to run after downloading them all? (There are some tricks but it doesn’t download all files in parallel, only third party DLLs)

    • Daniel RothMicrosoft employee 0

      Hi Ömer.

      The “Blazor United” effort is really a collection of features we’re adding to Blazor so that you can get the best of server & client based web development. These features include: Server-side rendering, streaming rendering, enhanced navigations & form handling, add client interactivity per page or component, and determining the client render mode at runtime. We’ve started delivering server-side rendering support for Blazor with .NET 8 Preview 3, which is now available to try out. We plan to deliver the remaining features in upcoming previews. We hope to deliver them all for .NET 8, but we’ll see how far we get.

      App size can be a concern for Blazor WebAssembly app, but your situation sounds a bit extreme: 20MB is a lot! Is that the uncompressed transfer size? A minimal Blazor WebAssembly app once it has been trimmed and compressed is only about 1MB. Are you perhaps pulling in some large dependencies that don’t support trimming and that would be best left on the server? I would examine your dependency graph and make sure you’re only pulling in what you absolutely need and that you work to make sure all your dependencies support trimming if possible. Also confirm that the app is actually getting trimmed and the compressed files are being served once published. The app files should be fetched in parallel to the extent that your browser and server environment supports that. The files will also be cached locally so that they don’t need to be fetched again on subsequent visits. See the Blazor host & deploy docs for further guidance on deploying Blazor WebAssembly apps.

  • Rod Macdonald 0

    Hi Daniel, continuing my Windows/web quest for a 2 part app, using Blazor Hybrid + Blazor Server, have just read on StavkOverflow it’s not possible to use SkiaSharp in Hybrid. Would you know if that’s correct and if there are any plans for change if it is? Thank you.

    • Daniel RothMicrosoft employee 0

      Hi Rod. What canvas are you trying to draw to? You can use SkiaSharp in a .NET MAUI app. But Blazor Hybrid apps render to an embedded web view control. You can’t in general use SkiaSharp to render to the web view from outside the web view. You can use SkiaSharp from within the web view via WebAssembly, but Blazor Hybrid apps don’t typically use WebAssembly. Instead, the .NET code runs outside the web view.

      • Rod Macdonald 0

        Hi Daniel, what I’m trying to do is pick a drawing api which ‘just works’ across the Blazor triad should I decide to switch up or down (server, wasm, hybrid – though server and hybrid are the current goal). I’m aiming to write a custom graph style as part of my app (not that I’m some kind of software architect, far from it – business apps are just fine with my limitations so it’s going to be difficult!) and I’ve read SVG is the more comprehensive route over HTMLCanvas, indeed there seem to be some good examples using React, but of course that’s pesky JavaScript. I thought SkiaSharp might come to my rescue, but clearly from what you advise, that’s no good unless Blazor Hybrid updates Blazor Server in some way with content routed and rendered back to Blazor Hybrid.

        Any direction would be much appreciated (common SVG drawing stack), and I ought to read your article ‘ASP.NET Core updates in .NET 6 Release Candidate 2’. Many thanks, Rod

  • Ricardo Francisco 0

    First of all, great work guys. There’s a lot of good stuff in this preview.

    AOT looks promising and it will be great addition when all most of the ecosystem is available (EF Core for example). But I realize this is still a preview so I’m hoping this will continue to improve and hope this won’t become another half-baked feature that is cool for marketing purposes but mostly useless in real day-to-day work like HotReload and Blazor support in VsCode.

    I’ve been pushing to move our primary development stack away from Node/Angular/React into .Net and so far I’ve had very good results in terms of the backend. Developers feel productive, they like it, and they see the end result is more stable and maintainable. But on the frontend side, things are not so good. Pushing for Blazor (that I realy like) is not easy due to several reasons: limited non-commercial ecosystem, performance, but mostly because of lack of proper support in vs-code – the preferred editor for everyone in our company. At this time vs-code support for Blazor is still lacking, with many performance, colorization, intellisense and formatting issues and the need to reload vs-code regularly. It’s a shame because the editor in visual studio for Mac works a lot better.

    • Daniel RothMicrosoft employee 0

      Hi Ricardo. Thanks for sharing this feedback with us! I have a few questions about your Blazor experience:
      1. What non-commercial components or libraries we’re you unable to find?
      2. What sorts of performance problems did you hit? What would it take for Blazor to meet your performance requirements?
      3. Did you try getting folks to use Visual Studio instead of Visual Studio Code? Was there anything that prevented using Visual Studio instead of Visual Studio Code?

      • Ricardo Francisco 0
        1. Mostly UI components libraries. Just do a web search for “blazor gantt, scheduler, google maps, charts” and you can see that most of the search results come from 3 of 4 commercial products. There’s nothing wrong with these products, except that they represent a big investment and commitment for small companies, especially if they are not a dedicated dotnet shop and you don’t want to pay thousands of dollars just to have a couple of components. For the most exotic components I usually end up creating my own or using javascript interop. And it’s frustrating to see that Microsoft has built a nice Fluent UI component library for react, but the FluentUI Blazor version only has a limited subset of features. Why is that?
        2. Related to Blazor Wasm itself it’s mostly the loading time. All is good when you start a project, but when you start adding third-party dependencies the download size increases a lot, and over time the applications start feeling sluggish. For some projects, especially if they are to be used internally by our clients, this is not a big issue.
        3. VS Code was a breath of fresh air. Having a blazing-fast IDE that can be used to develop anything is just great. I think this is one of MS best products. VS Code for Mac is more of a one-trick pony. It’s good for .Net projects, but that’s all. In repositories where you might have php, go, java, .net, terraform, angular/react it’s not a good fit. In vscode you can simply open the repo folder and work with any technology. So telling people to change the IDE they love just to be able to work with one particular technology is not the best option. I think it would make a lot more sense for MS to invest a little in VS Code support for Blazor instead of trying to push people into Visual Studio.
        • Daniel RothMicrosoft employee 0

          Thanks for sharing these additional details!

          > the FluentUI Blazor version only has a limited subset of features. Why is that?

          The Blazor Fluent UI component library is in the process of being expanded. See https://baaijte.net/blog/microsoft-fluentui-blazor-3.0/ for more details.

          > when you start adding third-party dependencies the download size increases a lot

          Yeah, you need to be mindful of your dependencies with Blazor WebAssembly. The .NET trimmer can trim these dependencies, but currently it will only do so if the library indicates it was designed for trimming. You can try reaching out to the library owner and see if adding trimming support is on their roadmap.

          > I think it would make a lot more sense for MS to invest a little in VS Code support for Blazor

          Makes sense. Improving the C# and Razor support in VS Code is something we’re working on.

  • Marcin Sulecki 0

    The idea of generating static pages using Blazor is great and I’ve been waiting for this.
    I would like to generate the entire page this way and use the layout. I tried using RenderComponentAsync to render @page with @layout but it only generates the component itself.

    Is there any way to generate the page based on the indicated @layout?

    • Daniel RothMicrosoft employee 0

      Hi Marcin. Layouts in Blazor are actually handled by specific components. For example we have:

      • LayoutView, which renders a specified RenderFragment inside a specified layout type (including any nested hierarchy of layouts)
      • RouteView, which takes a RouteData parameter and from that renders a nominated component, including passing parameters to it, and locating any associated layout and rendering the component inside that layout via LayoutView

      So, you need to render a root component that knows how to deal with rendering the layout. This isn’t super convenient right now. If you have thoughts on how we could improve things for your scenario please let us know by creating an issue on GitHub.

Feedback usabilla icon