ASP.NET Core updates in .NET 6 Preview 4

Daniel

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

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

  • Introducing minimal APIs
  • Async streaming
  • HTTP logging middleware
  • Use Kestrel for the default launch profile in new projects
  • IConnectionSocketFeature
  • Improved single-page app (SPA) templates
  • .NET Hot Reload updates
  • Generic type constraints in Razor components
  • Blazor error boundaries
  • Blazor WebAssembly ahead-of-time (AOT) compilation
  • .NET MAUI Blazor apps
  • Other performance improvements

Get started

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

If you’re on Windows using Visual Studio, we recommend installing the latest preview of Visual Studio 2019 16.11. If you’re on macOS, we recommend installing the latest preview of Visual Studio 2019 for Mac 8.10.

Upgrade an existing project

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

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

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

Introducing minimal APIs

In .NET 6, we are introducing minimal APIs for hosting and routing in web applications. This opens the doors for new developers building their first web application with .NET and to our existing customers who want to build small microservices and HTTP APIs. These streamlined APIs provide the benefits of ASP.NET MVC with less ceremony.

To try out creating a minimal API, create a new ASP.NET Core empty web app.

dotnet new web -o MinApi

With just a single file and a few lines of code, you now have a fully functioning HTTP API:

miniapi-preview

New routing APIs

The new routing APIs allow users to route to any type of method. These methods can use controller-like parameter binding, JSON formatting, and action result execution.

Before (using the existing Map APIs):

app.MapGet("/", async httpContext =>
{
    await httpContext.Response.WriteAsync("Hello World!");
});

After (using the new Map overloads):

app.MapGet("/", (Func<string>)(() => "Hello World!"));

C# 10 improvements

These APIs already take advantage of newer C# features, like top-level statements. With C# 10, which ships with .NET 6 later this year, the experience will get even better. For example, the explicit cast to (Func<string>) will no longer be necessary. The image below demonstrates what it will look like once all of the C# 10 features are implemented.

miniapi-functions

The developer goes from using classes and methods to using lambdas without giving up the ability to use attributes and other features available to MVC Controller actions.

New hosting APIs

The new empty web template is using the new hosting model introduced in .NET 6 Preview 4.

var app = WebApplication.Create(args);

app.MapGet("/", (Func<string>)(() => "Hello World!"));

app.Run();

You aren’t limited to just use the new routing APIs. Below is an example of an existing web app updated to use the new hosting model that configures services and adds middleware.

using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
builder.Services.AddSwaggerGen(c =>
{
    c.SwaggerDoc("v1", new OpenApiInfo { Title = "Api", Version = "v1" });
});

var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
    app.UseSwagger();
    app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Api v1"));
}

app.UseHttpsRedirection();
app.UseAuthorization();
app.MapControllers();

app.Run();

The new hosting API reduces the amount of boilerplate required to the configure and start any ASP.NET app.

Performance

These new routing APIs have far less overhead than controller-based APIs. Using the new routing APIs, ASP.NET Core is able to achieve ~800k RPS in the TechEmpower JSON benchmark vs ~500k RPS for MVC.

Tech-Empower-Benchmark

Async streaming

ASP.NET Core now supports async streaming from controller actions all the way down to the response JSON formatter. Returning an IAsyncEnumerable from an action no longer buffers the response content in memory before it gets sent. This helps reduce memory usage when returning large datasets that can be asynchronously enumerated.

Note that Entity Framework Core provides implementations of IAsyncEnumerable for querying the database. The improved support for IAsyncEnumerable in ASP.NET Core in .NET 6 can make using EF Core with ASP.NET Core more efficient. For example, the following code will no longer buffer the product data into memory before sending the response:

public IActionResult GetProducts()
{
    return Ok(dbContext.Products);
}

However, if you have setup EF Core to use lazy loading, this new behavior may result in errors due to concurrent query execution while the data is being enumerated. You can revert back to the previous behavior by buffering the data yourself:

public async Task<IActionResult> Products()
{
    return Ok(await dbContext.Products.ToListAsync());
}

See the related announcement for additional details about this change in behavior.

HTTP logging middleware

HTTP logging is a new built-in middleware that logs information about HTTP requests and HTTP responses including the headers and entire body.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
    app.UseHttpLogging();
}

HTTP logging provides logs of:

  • HTTP Request information
  • Common properties
  • Headers
  • Body
  • HTTP Response information

To configure the HTTP logging middleware, you can specify HttpLoggingOptions in your call to ConfigureServices():

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpLogging(logging =>
    {
        // Customize HTTP logging here.
        logging.LoggingFields = HttpLoggingFields.All;
        logging.RequestHeaders.Add("My-Request-Header");
        logging.ResponseHeaders.Add("My-Response-Header");
        logging.MediaTypeOptions.AddText("application/javascript");
        logging.RequestBodyLogLimit = 4096;
        logging.ResponseBodyLogLimit = 4096;
    });
}

This results in a new log with the HTTP request information in the Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware category.

info: Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware[1]
      Request:
      Protocol: HTTP/1.1
      Method: GET
      Scheme: https
      PathBase:
      Path: /
      QueryString:
      Connection: keep-alive
      Accept: */*
      Accept-Encoding: gzip, deflate, br
      Host: localhost:5001
      User-Agent: PostmanRuntime/7.26.5
      My-Request-Header: blogpost-sample
      Postman-Token: [Redacted]

For more information on how to use HTTP logging, take a look at the HTTP logging docs.

Use Kestrel for the default launch profile in new projects

Kestrel is default launch profile

We’ve changed the default launch profile from IIS Express to Kestrel for all new projects created in .NET 6 Preview 4. Starting Kestrel is significantly faster and results in a more responsive experience while developing your apps.

  IIS Express (ms) Kestrel (ms) % change
Debugging 4359 2772 36%
No debugging 1999 727 64%


IIS Express is still available as a launch profile for scenarios such as Windows Authentication or port sharing.

IConnectionSocketFeature

The IConnectionSocketFeature request feature gives you access to the underlying accept socket associated with the current request. It can be accessed via the FeatureCollection on HttpContext.

For example, the application below sets the LingerState property on the accepted socket:

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(serverOptions =>
{
    serverOptions.ConfigureEndpointDefaults(listenOptions => listenOptions.Use((connection, next) =>
    {
        var socketFeature = connection.Features.Get<IConnectionSocketFeature>();
        socketFeature.Socket.LingerState = new LingerOption(true, seconds: 10);
        return next();
    }));
});
var app = builder.Build();
app.MapGet("/", (Func<string>)(() => "Hello world"));
await app.RunAsync();

Improved single-page app (SPA) templates

We’ve updated the ASP.NET Core project templates for Angular and React to use a improved pattern for single-page apps that is more flexible and more closely aligns with common patterns for modern front-end web development.

Previously, the ASP.NET Core template for Angular and React used specialized middleware during development to launch the development server for the front-end framework and then proxy requests from ASP.NET Core to the development server. The logic for launching the front-end development server was specific to the command-line interface for the corresponding front-end framework. Supporting additional front-end frameworks using this pattern meant adding additional logic to ASP.NET Core.

The updated ASP.NET Core templates for Angular and React in .NET 6 flips this arrangement around and take advantage of the built-in proxying support in the development servers of most modern front-end frameworks. When the ASP.NET Core app is launched, the front-end development server is launched just as before, but the development server is configured to proxy requests to the backend ASP.NET Core process. All of the front-end specific configuration to setup proxying is part of the app, not ASP.NET Core. Setting up ASP.NET Core projects to work with other front-end frameworks is now straight-forward: simply setup the front-end development server for your framework of choice to proxy to the ASP.NET Core backend using the pattern established in the Angular and React templates.

The startup code for the ASP.NET Core app no longer needs any single-page app specific logic. The logic for starting the front-end development server during development is injecting into the app at runtime by the new Microsoft.AspNetCore.SpaProxy package. Fallback routing is handled using endpoint routing instead of SPA specific middleware.

Templates that follow this pattern can still be run as a single project in Visual Studio or using dotnet run from the command-line. When the app is published, the front-end code in the ClientApp folder is built and collected as before into the web root of the host ASP.NET Core app and served as static files. Scripts included in the template configure the front-end development server to use HTTPS using the ASP.NET Core development certificate.

.NET Hot Reload updates

The latest preview of Visual Studio has some initial support for .NET Hot Reload. You may have noticed the new Apply Code Changes button and debug option when debugging your app:

Apply Code Changes button

The Apply Code Changes button will update the running app with any code changes you’ve made in the editor even without having to save the file. Here’s an example of updating the Counter component to increment by two instead of one. Notice that the current count is not lost once the change has been applied.

.NET Hot Reload in Visual Studio with Blazor

.NET Hot Reload support in Visual Studio is still a work in progress, so there are a number of limitations when using it with ASP.NET Core apps:

  • You must run with the debugger attached to apply changes
  • Code changes can only be applied to C# files – changes to Razor files (.razor, .cshtml) don’t work yet
  • Applied changes don’t force the UI to update yet, so UI updates will need to be triggered manually
  • Changes can’t be applied to Blazor WebAssembly apps yet

All of these limitations are being worked and will be addressed in future Visual Studio updates. Stay tuned!

If you’re using .NET Hot Reload via dotnet watch, changes will now be applied to ASP.NET Core hosted Blazor WebAssembly apps. Changes will also be reapplied to your Blazor WebAssembly app if you refresh the browser.

To learn more about .NET Hot Reload you can get all the details in our blog post: Introducing .NET Hot Reload

Generic type constraints in Razor

When defining generic type parameters in Razor using the @typeparam directive, you can now specify generic type constraints using the standard C# syntax:

@typeparam TEntity where TEntity : IEntity

Blazor error boundaries

Blazor error boundaries provide a convenient way to handle exceptions within the component hierarchy. To define an error boundary, use the new ErrorBoundary component to wrap some existing content. The ErrorBoundary component will render its child content as long as everything is running smoothly. If an unhandled exception is thrown, the ErrorBoundary renders some error UI.

For example, we can add an error boundary around the body content of the layout of the default Blazor app like this:

<div class="main">
    <div class="top-row px-4">
        <a href="https://docs.microsoft.com/aspnet/" target="_blank" rel="noopener">About</a>
    </div>

    <div class="content px-4">
        <ErrorBoundary>
            @Body
        </ErrorBoundary>
    </div>
</div>

The app continues to function as before but now our error boundary will handle unhandled exceptions. For example, we can update the Counter component to throw an exception if the count gets too big.

private void IncrementCount()
{
    currentCount++;
    if (currentCount > 10)
    {
        throw new InvalidOperationException("Current count is too big!");
    }
}

Now if we click the counter too much, an unhandled exception is thrown, which gets handled by our error boundary by rendering some default error UI:

Blazor error boundary default UI

By default, the ErrorBoundary component renders an empty div with a blazor-error-boundary CSS class for its error content. The colors, text, and icon for this default UI all defined using CSS in the app, so you are free to customize them. You can also change the default error content by setting the ErrorContent property.

<ErrorBoundary>
    <ChildContent>
        @Body
    </ChildContent>
    <ErrorContent>
        <p class="my-error">Nothing to see here right now. Sorry!</p>
    </ErrorContent>
</ErrorBoundary>

Because we defined the error boundary in the layout, once an exception is thrown we now only see the error content regardless of which page we navigate to. It’s generally best to scope error boundaries more narrowly than this, but we can choose to reset the error boundary to a non-error state on subsequent page navigations by calling the Recover method on the error boundary.

...
<ErrorBoundary @ref="errorBoundary">
    @Body
</ErrorBoundary>
...

@code {
    ErrorBoundary errorBoundary;

    protected override void OnParametersSet()
    {
        // On each page navigation, reset any error state
        errorBoundary?.Recover();
    }
}

Blazor WebAssembly ahead-of-time (AOT) compilation

Blazor WebAssembly now supports ahead-of-time (AOT) compilation, where you can compile your .NET code directly to WebAssembly for a significant runtime performance improvement. Blazor WebAssemby apps today run using a .NET IL interpreter implemented in WebAssembly. Because the .NET code is interpreted, typically this means that .NET code running on WebAssembly runs much slower than it would on a normal .NET runtime. .NET WebAssembly AOT compilation addresses this performance issue by compiling your .NET code directly to WebAssembly.

The performance improvement from AOT compiling your Blazor WebAssembly app can be quite dramatic for CPU intensive tasks. For example, the clip below shows a comparison of doing some basic image editing using the same Blazor WebAssembly app, first using the interpreter and then AOT compiled. The AOT compiled version runs over five times faster!

Picture Fixer AOT

You can check out the code for this PictureFixer on GitHub.

.NET WebAssembly AOT compilation requires an additional build tools that must be installed as an optional .NET SDK workload in order to use. To install the .NET WebAssembly build tools, run the following command from an elevated command prompt:

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

To enable WebAssembly AOT compilation in your Blazor WebAssembly project, add the following property to your project file:

<RunAOTCompilation>true</RunAOTCompilation>

To then AOT compile your app to WebAssembly, publish the app. Publishing using the Release configuration will ensure the .NET IL linking is also run to reduce the size of the published app:

dotnet publish -c Release

WebAssembly AOT compilation is only performed when the the project is published. It isn’t used when the project is run during development. This is because WebAssembly AOT compilation can take a while: several minutes on small projects and potentially much longer for larger projects. Speeding up the build time for WebAssembly AOT compilation is something that we are working on.

The size of an AOT compiled Blazor WebAssembly app is generally larger than the if the app was left as .NET IL. In our testing, most AOT compiled Blazor WebAssembly apps are about 2x larger, although it depends on the specific app. This means that using WebAssembly AOT compilation trades off load time performance for runtime performance. Whether this tradeoff is worth it will depend on your app. Blazor WebAssembly apps that are particularly CPU intensive will benefit the most from AOT compilation.

.NET MAUI Blazor apps

Blazor enables building client-side web UI with .NET, but sometimes you need more than what the web platform offers. Sometimes you need full access to the native capabilities of the device. You can now host Blazor components in .NET MAUI apps to build cross-platform native apps using web UI. The components run natively in the .NET process and render web UI to an embedded web view control using a local interop channel. This hybrid approach gives you the best of native and the web. Your components can access native functionality through the .NET platform, and they render standard web UI. .NET MAUI Blazor apps can run anywhere .NET MAUI can (Windows, Mac, iOS, and Android) although our primary focus for .NET 6 is on desktop scenarios.

To create a .NET MAUI Blazor app, you first need to set up .NET MAUI on your development machine. The easiest way to do that is using the maui-check tool.

To install the maui-check tool, run:

dotnet tool install -g Redth.Net.Maui.Check

Then run maui-check to acquire the .NET MAUI tooling and dependencies.

Add the following NuGet feed to get access to the .NET MAUI packages:

dotnet nuget add source -n maui-preview https://aka.ms/maui-preview/index.json

For additional information about getting started with .NET MAUI, refer to the wiki documentation on GitHub.

Once everything is installed, create a .NET MAUI Blazor app using the new project template:

dotnet new maui-blazor -o MauiBlazorApp

You can also create a .NET MAUI Blazor app using Visual Studio:

New .NET MAUI Blazor app

.NET MAUI Blazor apps are .NET MAUI apps that use a BlazorWebView control to render Blazor components to an embedded web view. The app code and logic lives in the MauiApp project, which is set up to multi-target Android, iOS, and Mac Catalyst. The MauiApp.WinUI3 project is used to build for Windows, and the MauiApp.WinUI3 (Package) project is used to generate an MSIX package for Windows. Eventually we expect to merge the Windows support into the main app project, but for now these separate projects are necessary.

The BlazorWebView control is set up for you in MainPage.xaml in the MauiApp project:

<b:BlazorWebView HostPage="wwwroot/index.html">
    <b:BlazorWebView.RootComponents>
        <b:RootComponent Selector="#app" ComponentType="{x:Type local:Main}" />
    </b:BlazorWebView.RootComponents>
</b:BlazorWebView>

The root Blazor component for the app is in Main.razor. The rest of the Blazor components are in the Pages and Shared directories. Notice that these components are the same ones used in the default Blazor template. You can use existing Blazor components in your app unchanged either by moving the code into the app, or by referencing an existing class library or package that contains the components. Static web assets for the app are in the wwwroot folder.

Windows

To run the app for Windows, you’ll need to build and run using Visual Studio.

Select the MauiBlazorApp.WinUI3 (Package) project as your startup project

Select WinUI startup project

Also select x64 for the target platform.

Select x64 target platform

You can then hit F5 or Ctrl+F5 to run the app as a native Windows desktop app using WinUI.

.NET MAUI Blazor app running on Windows

Android

To run the app on Android, first start the Android emulator using the Android SDK or the Android Device Manager.

Then run the app from the CLI using the following command:

dotnet build MauiBlazorApp -t:Run -f net6.0-android

To run on Android from Visual Studio, select the MauiBlazorApp project as the startup project

Select startup project

Then select net6.0-android as the target framework in the Run button drop-down menu.

Select Android target framework

You can then hit F5 or Ctrl+F5 to run the app using the Android emulator.

Android

iOS and Mac Catalyst

To run the app for iOS or Mac Catalyst, you’ll need to use a macOS development environment running Big Sur. You can’t currently run the app for iOS or Mac Catalyst from a Windows development environment, although we do expect that .NET MAUI will support running iOS apps using a connected Mac build agent or on a connected device using Hot Restart.

To run the app for iOS and Mac Catalyst, use the following commands:

dotnet build MauiBlazorApp -t:Run -f net6.0-ios
dotnet build MauiBlazorApp -t:Run -f net6.0-maccatalyst

iOS and Mac Catalyst

There are some known limitations with .NET MAUI Blazor apps in this release:

  • Component scoped CSS files (.razor.css) are not working yet in the main .NET MAUI project. This will get fixed in a future update.

Learn more about what’s new in .NET MAUI in .NET 6 Preview 4.

Other performance improvements

As part of continually improving the performance of ASP.NET Core, we made a whole host changes to reduce allocations and improve performance across the stack:

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!

51 comments

Comments are closed. Login to edit/delete your existing comments

  • Josh Dadak

    Awesome release, really pleased to see the AOT stuff coming out.
    Is there any advice on how to run

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

    as part of our build pipelines?

  • Prem Shah

    Hello,
    I am very excited about .NetMauiBlazor App.
    Please add BlazorServer and Blazor WASM As Platform in .NetMauiBlazor Project so we don’t have to create one for server , one for WASM and one for Maui. (still developer have to manage 3 projects)
    so developer can deploye for all platform like server, wasm, IOS,Mac and windows from Single project rather than creating 3 projects.

    • Daniel RothMicrosoft employee

      Hi Prem. Thanks for this suggestion! While it’s possible to host Blazor components in a .NET MAUI project, Blazor Server and Blazor WebAssembly projects are just normal web projects, so they can’t be combined with the .NET MAUI project. At some future point it may be possible to target a .NET MAUI project to the web, although that isn’t planned for .NET 6. If that’s a scenario that’s interesting to you, give a 👍 on GitHub to https://github.com/dotnet/maui/issues/1069.

  • Gabriel Guivisdalsky

    Hello, great changes. One question, what about error management and models, it is supposed to do that now in a service layer since controller layer disappear ?

    • Stephen HalterMicrosoft employee

      The new routing APIs (e.g. the new MapGet/MapPost/… overloads) are not intended to give you all the features of MVC Controllers even though there are some similarities. Model binding and validation is something that’s still in the design phase. You can handle exceptions thrown form callbacks similar to any other exception thrown from middleware and you can manually validate your inputs.

      now in a service layer since controller layer disappear

      Controllers are not disappearing. You can continue to use Controllers if that better fits your app. If you want to try out the new routing APIs, I would try putting the logic that used to live in controllers directly in the callbacks instead. However, for certain things, moving the logic to services might be cleaner.

  • Daniel Pach

    Congratulations Dan, this is excellent progress!

    Couple of questions:

    1. AOT speed improvements – AOT demo shows about 5x speed improvement over the interpreted mode. Honestly, this is a bit disappointing – I was expecting something close to 50x-100x! Is this something that is going to improve in the next few iterations?

    2. AOT size increase – 2x is quite a bit given that the large size is already one of the two major disadvantages of Blazor (besides speed of execution) compared to other UI frameworks. Can we expect that the size will decrease in the future versions? What is the status of https://github.com/dotnet/runtimelab/tree/feature/NativeAOT project? Also, my understanding is that a major contributor to the initial Blazor size is GC – which will be eventually handled by WASM. How much of a size reduction will this yield?

    3. Blazor Hybrid – we are investigating which is the best model to deploy a mobile version of our product. Initially, our thoughts were PWA, but not sure if there is now a better option. Does Blazor Hybrid mean that we could deploy the same Blazor WASM application that we are developing for web to Android or iOS by simply wrapping the app inside a webview? The app will then be compiled into native code, correct? Is this going to be more performant over PWA? Do you have any basic benchmarks?

    Thank you!

    • David Taylor

      Hi Daniel, I will have a go at answering your questions.
      1. You would expect to get more than 5x speed improvement with AOT to WASM (compared to interpreted) if you were doing CPU intensive work. For real world projects it will depend on where the bottlenecks are (rendering, interop with javascript libraries, etc).
      2. The WASM format under AOT ends up larger than shipping a .NET IL DLL. They are working to improve (decrease) the size. The final part of your question about browser’s exposing their own native GC to web assembly (many years in the future): It will realistically be many years before all (>95%) browsers have a versions of web assembly exposing a garbage collector in the way you are thinking. I am excited by it, but realistically it wont be much use to us until say 2025 or later.
      3. Blazor Hybrid means the Blazor .NET code is running wherever the Maui code is executing, instead of running within the browser on WASM. In this case WASM has nothing to do with it. For example, if you were running your Blazor app on Windows or Mac, the full .NET 6 runtime would be executing your code. In terms of benchmarks you would get extremely close to knowing final performance simply by running your code using .NET Blazor server on localhost. Although obviously it will be even better than that because it does not need to use a signalr connection over a websocket – so lower latency between the .NET side and the browser renderer in Hybrid.
      Regards,
      David

      • Daniel RothMicrosoft employee

        Hi Daniel,

        Could you please share with us the scenarios where you were expecting a larger performance boost from WebAssembly AOT? It would be easiest if you could share specific benchmark scenarios on GitHub that we can test: https://github.com/dotnet/aspnetcore/issues/new.

        We are actively working on reducing the download size of Blazor WebAssembly apps. Published Blazor WebAssembly apps with .NET 6 are already about 10% smaller than the same app published with .NET 5. We expect further improvements (~5-10%) before we ship in Nov later this year.

        The NativeAOT project is an experimental effort, and isn’t part of .NET 6. The .NET WebAssembly AOT support in .NET 6 is based on the production AOT toolchain used also for .NET MAUI and Xamarin.

        There are a number of improvements to the WebAssembly standard that would help improve the experience with .NET, but these are long lead efforts.

        A .NET MAUI Blazor app will have better performance than running on WebAssembly. In a .NET MAUI Blazor app your Blazor components run directly in the native .NET process for the app. They don’t run on WebAssembly and they are not constrained by the browser security sandbox. This means your components run fast and have full access to the underlying platform through normal .NET APIs.

  • Tomasz Grzmilas

    Daniel,

    Thanks to you and the whole team, for great work. I’m reading those blogs and every new one make me more happy to use .NET !

  • Alvaro Rivoir

    I followed all the steps to create an AOT application, but the browser is still downloading dll’s. is that expected? How can I check the application is compiled to WASM?

    • Daniel RothMicrosoft employee

      Hi Alvaro. Yes, you will still see dll’s downloaded with the app even when using WebAssembly AOT compilation. This is because in some situations we still fall back to the .NET IL interpreter for functionality that isn’t support with AOT.

  • Rod Macdonald

    I didn’t properly know about Fluent UI until I saw it in Build, 1st a useful comment from Scott H, then it appeared on your presentation, so that is very useful. A common set of controls that can be adapted for the appropriate environment has to be a really useful stack. The one thing that is missing are ‘vectorisable’ controls for the web! In fact, such a stack would work in any environment?

  • Sander ten Brinke

    Great stuff! How can I try out these new SPA templates?

    And a bonus question, how would I implement a BFF in .NET 6 like this:

    • before you access the spa, you need to be logged in
    • spa runs on /
    • if you go to / but are not logged in, you are shown a bff-server-side-rendered login screen

    Currently I have the issue that my HomeController has an authorize and authorizeforscopes tag (azure ad login) and then redirects to (/home) which is the spa. But I want the spa to run on / but that is where the homecontroller intercepts the request to check for auth. So I can’t serve the spa on “/”

    But I want the user to be brought to /login if they are not logged in or have not authorized all scopes. /Login shows a “log in with ms account” button and that will then auth you and finally bring you to spa 🙂

    (How) do these new templates help me with this?

  • notbad

    I tried to publish the project with aot enabled, but got this error:

    $ dotnet publish -c Release
    Microsoft (R) Build Engine version 16.11.0-preview-21254-21+e73d08c28 for .NET
    Copyright (C) Microsoft Corporation. All rights reserved.
    
      Determining projects to restore...
      All projects are up-to-date for restore.
      You are using a preview version of .NET. See: https://aka.ms/dotnet-core-preview
      CSharpBenchmark -> D:\Benchmark\CSharpBenchmark\bin\Release\net5.0\CSharpBenchmark.dll
      BlazorWasmDotNet6Benchmark -> D:\Benchmark\BlazorWasmDotNet6Benchmark\bin\Release\net6.0\BlazorWasmDotNet6Benchmark.dll
      BlazorWasmDotNet6Benchmark (Blazor output) -> D:\Benchmark\BlazorWasmDotNet6Benchmark\bin\Release\net6.0\wwwroot
      Optimizing assemblies for size, which may change the behavior of the app. Be sure to test after publishing. See: https://aka.ms/dotnet-illink
      AOT'ing 32 assemblies
      Compiling native assets with emcc. This may take a while ...
      Traceback (most recent call last):
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\emcc.py", line 3315, in 
          sys.exit(main(sys.argv))
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\emcc.py", line 3308, in main
          ret = run(args)
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\emcc.py", line 2156, in run
          post_link(options, wasm_target, wasm_target, target)
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\emcc.py", line 2185, in post_link
          emscripten.run(in_wasm, wasm_target, final_js, memfile)
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\emscripten.py", line 876, in run
          return temp_files.run_and_clean(lambda: emscript(
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\tools\tempfiles.py", line 104, in run_and_clean
          return func()
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\emscripten.py", line 876, in 
          return temp_files.run_and_clean(lambda: emscript(
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\emscripten.py", line 331, in emscript
          glue, forwarded_data = compile_settings(temp_files)
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\emscripten.py", line 195, in compile_settings
          out = shared.run_js_tool(path_from_root('src', 'compiler.js'),
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\tools\shared.py", line 118, in run_js_tool
          return check_call(command, *args, **kw).stdout
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\tools\shared.py", line 104, in check_call
          return run_process(cmd, *args, **kw)
        File "C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.Emscripten.2.0.12.Sdk.win-x64\6.0.0-preview.4.21220.1\tools\emscripten\tools\shared.py", line 94, in run_process
          ret = subprocess.run(cmd, check=check, input=input, *args, **kw)
        File "subprocess.py", line 507, in run
        File "subprocess.py", line 1121, in communicate
      UnicodeDecodeError: 'gbk' codec can't decode byte 0x94 in position 325809: illegal multibyte sequence
    C:\Program Files\dotnet\packs\Microsoft.NET.Runtime.WebAssembly.Sdk\6.0.0-preview.4.21253.7\Sdk\WasmApp.targets(504,5): error MSB3073: The command "emcc "@D:\Benchmark\BlazorWasmDotNet6Benchmark\obj\Release\net6.0\wasm\emcc-link.rsp"" exited with code 1. [D:\Benchmark\BlazorWasmDotNet6Benchmark\BlazorWasmDotNet6Benchmark.csproj]
  • zephania Eliah

    Hello Daniel
    This is awesome release for us especially Blazor Maui as it will be easy to use local db like LiteDB to store data locally and sync later, one bottleneck we have faced while trying out is how to use js interop as IJSruntime is not part of Blazor Maui or if we are missing which nuget package we have to use to bring in IJSRuntime or what is the work arround if there is no way to obtain it from nuget package…

    • Daniel RothMicrosoft employee

      Hi Zephania. Using IJSRuntime to do JavaScript interop should still work in a .NET MAUI app. The JavaScript interop calls should get forwarded to the browser control. If you’re seeing issues with this, please open a GitHub issue with details on what steps you are following and the exact error you are seeing: https://github.com/dotnet/aspnetcore/issues/new.