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

    • Benoit Gerard

      It is still planned for 6.0 GA. It seems they wait for it to be discussed in an Design Review meeting before allocating it in a preview.

      Let’s hope it will prevent any other loop hole in this manager as I concur, it is indeed much needed.

      • Daniel RothMicrosoft employee

        Thanks for the feedback! Make sure you’ve given the issue a 👍 on GitHub if you haven’t already as this helps us prioritize the most impactful work.

  • ThopDev

    I tried the aot but when installing the

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

    i get the following error:

    Installing pack Microsoft.NET.Runtime.MonoAOTCompiler.Task version 6.0.0-preview.4.21253.7...
    Pack Microsoft.NET.Runtime.MonoAOTCompiler.Task version 6.0.0-preview.4.21253.7 is already installed.
    Writing workload pack installation record for Microsoft.NET.Runtime.MonoAOTCompiler.Task version 6.0.0-preview.4.21253.7...
    System.UnauthorizedAccessException: Access to the path 'C:\Program Files\dotnet\metadata\workloads\InstalledPacks\v1\Microsoft.NET.Runtime.MonoAOTCompiler.Task\6.0.0-preview.4.21253.7\6.0.100' is denied.
       at System.IO.FileSystem.DeleteFile(String fullPath) in System.IO.FileSystem.dll:token 0x60001ac+0x15
       at System.IO.File.Delete(String path) in System.IO.FileSystem.dll:token 0x60000f3+0xe
       at Microsoft.DotNet.Workloads.Workload.Install.NetSdkManagedInstaller.DeletePackInstallationRecord(PackInfo packInfo, SdkFeatureBand featureBand) in dotnet.dll:token 0x60001c7+0x11
       at Microsoft.DotNet.Workloads.Workload.Install.NetSdkManagedInstaller.RollBackWorkloadPackInstall(PackInfo packInfo, SdkFeatureBand sdkFeatureBand) in dotnet.dll:token 0x60001bd+0x0
       at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.c__DisplayClass14_1.b__3() in dotnet.dll:token 0x6000b20+0x15
       at Microsoft.DotNet.Cli.TransactionalAction.EnlistmentNotification.Rollback(Enlistment enlistment) in dotnet.dll:token 0x6000ced+0x8
       at System.Transactions.VolatileEnlistmentAborting.EnterState(InternalEnlistment enlistment) in System.Transactions.Local.dll:token 0x6000404+0x0
       at System.Transactions.VolatileEnlistmentActive.InternalAborted(InternalEnlistment enlistment) in System.Transactions.Local.dll:token 0x60003e9+0x0
       at System.Transactions.TransactionStateAborted.EnterState(InternalTransaction tx) in System.Transactions.Local.dll:token 0x6000259+0x4c
       at System.Transactions.TransactionStateActive.Rollback(InternalTransaction tx, Exception e) in System.Transactions.Local.dll:token 0x6000224+0x8
       at System.Transactions.Transaction.Rollback() in System.Transactions.Local.dll:token 0x600010b+0x4b
       at System.Transactions.TransactionScope.InternalDispose() in System.Transactions.Local.dll:token 0x600019c+0x95
       at System.Transactions.TransactionScope.Dispose() in System.Transactions.Local.dll:token 0x600019b+0x34b
       at Microsoft.DotNet.Cli.TransactionalAction.Run[T](Func`1 action, Action commit, Action rollback) in dotnet.dll:token 0x6000922+0x47
       at Microsoft.DotNet.Cli.TransactionalAction.Run(Action action, Action commit, Action rollback) in dotnet.dll:token 0x6000923+0x19
       at Microsoft.DotNet.Workloads.Workload.Install.NetSdkManagedInstaller.InstallWorkloadPack(PackInfo packInfo, SdkFeatureBand sdkFeatureBand, Boolean useOfflineCache) in dotnet.dll:token 0x60001bc+0x7e
       at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.c__DisplayClass14_1.b__2() in dotnet.dll:token 0x6000b1f+0x15
       at Microsoft.DotNet.Cli.TransactionalAction.c__DisplayClass2_0.b__0() in dotnet.dll:token 0x6000cef+0x0
       at Microsoft.DotNet.Cli.TransactionalAction.Run[T](Func`1 action, Action commit, Action rollback) in dotnet.dll:token 0x6000922+0x35
       at Microsoft.DotNet.Cli.TransactionalAction.Run(Action action, Action commit, Action rollback) in dotnet.dll:token 0x6000923+0x19
       at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.InstallWorkloadsWithInstallRecord(IEnumerable`1 workloadIds, SdkFeatureBand sdkFeatureBand) in dotnet.dll:token 0x60001cf+0xb5
       at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.InstallWorkloads(IEnumerable`1 workloadIds, Boolean skipManifestUpdate) in dotnet.dll:token 0x60001ce+0x58
       at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.Execute() in dotnet.dll:token 0x60001cb+0x216
       at Microsoft.DotNet.Cli.DotNetTopLevelCommandBase.RunCommand(String[] args) in dotnet.dll:token 0x60008af+0x5e
       at Microsoft.DotNet.Workloads.Workload.WorkloadCommand.Run(String[] args) in dotnet.dll:token 0x6000145+0x6
       at Microsoft.DotNet.Cli.Program.ProcessArgs(String[] args, TimeSpan startupTime, ITelemetry telemetryClient) in dotnet.dll:token 0x6000918+0x2e8
       at Microsoft.DotNet.Cli.Program.Main(String[] args) in dotnet.dll:token 0x6000916+0x6f

    I tried restarting the pc. I used powershell, cmd and package manger console in vs all as admin.

      • Steve

        I think it’s caused by the missing package on nuget:

        Installing pack Microsoft.NET.Runtime.Emscripten.Node version 6.0.0-preview.4.21220.1...
        [NuGet Manager] [Info]   GET https://api.nuget.org/v3/registration5-gz-semver2/microsoft.net.runtime.emscripten.2.0.12.node.win-x64/index.json
        [NuGet Manager] [Info]   NotFound https://api.nuget.org/v3/registration5-gz-semver2/microsoft.net.runtime.emscripten.2.0.12.node.win-x64/index.json

        In the end it failed to rollback and caused the problem:

        System.IO.IOException: The process cannot access the file 'C:\Program Files\dotnet\metadata\workloads\InstalledPacks\v1\Microsoft.NET.Runtime.MonoAOTCompiler.Task\6.0.0-preview.4.21253.7\6.0.100' because it is being used by another process.
           at System.IO.FileSystem.DeleteFile(String fullPath) in System.IO.FileSystem.dll:token 0x60001ac+0x15
           at System.IO.File.Delete(String path) in System.IO.FileSystem.dll:token 0x60000f3+0xe
           at Microsoft.DotNet.Workloads.Workload.Install.NetSdkManagedInstaller.DeletePackInstallationRecord(PackInfo packInfo, SdkFeatureBand featureBand) in dotnet.dll:token 0x60001c7+0x11
           at Microsoft.DotNet.Workloads.Workload.Install.NetSdkManagedInstaller.RollBackWorkloadPackInstall(PackInfo packInfo, SdkFeatureBand sdkFeatureBand) in dotnet.dll:token 0x60001bd+0x0
           at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.c__DisplayClass14_1.b__3() in dotnet.dll:token 0x6000b20+0x15
           at Microsoft.DotNet.Cli.TransactionalAction.EnlistmentNotification.Rollback(Enlistment enlistment) in dotnet.dll:token 0x6000ced+0x8
           at System.Transactions.VolatileEnlistmentAborting.EnterState(InternalEnlistment enlistment) in System.Transactions.Local.dll:token 0x6000404+0x0
           at System.Transactions.VolatileEnlistmentActive.InternalAborted(InternalEnlistment enlistment) in System.Transactions.Local.dll:token 0x60003e9+0x0
           at System.Transactions.TransactionStateAborted.EnterState(InternalTransaction tx) in System.Transactions.Local.dll:token 0x6000259+0x4c
           at System.Transactions.TransactionStateActive.Rollback(InternalTransaction tx, Exception e) in System.Transactions.Local.dll:token 0x6000224+0x8
           at System.Transactions.Transaction.Rollback() in System.Transactions.Local.dll:token 0x600010b+0x4b
           at System.Transactions.TransactionScope.InternalDispose() in System.Transactions.Local.dll:token 0x600019c+0x95
           at System.Transactions.TransactionScope.Dispose() in System.Transactions.Local.dll:token 0x600019b+0x34b
           at Microsoft.DotNet.Cli.TransactionalAction.Run[T](Func`1 action, Action commit, Action rollback) in dotnet.dll:token 0x6000922+0x47
           at Microsoft.DotNet.Cli.TransactionalAction.Run(Action action, Action commit, Action rollback) in dotnet.dll:token 0x6000923+0x19
           at Microsoft.DotNet.Workloads.Workload.Install.NetSdkManagedInstaller.InstallWorkloadPack(PackInfo packInfo, SdkFeatureBand sdkFeatureBand, Boolean useOfflineCache) in dotnet.dll:token 0x60001bc+0x7e
           at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.c__DisplayClass14_1.b__2() in dotnet.dll:token 0x6000b1f+0x15
           at Microsoft.DotNet.Cli.TransactionalAction.c__DisplayClass2_0.b__0() in dotnet.dll:token 0x6000cef+0x0
           at Microsoft.DotNet.Cli.TransactionalAction.Run[T](Func`1 action, Action commit, Action rollback) in dotnet.dll:token 0x6000922+0x35
           at Microsoft.DotNet.Cli.TransactionalAction.Run(Action action, Action commit, Action rollback) in dotnet.dll:token 0x6000923+0x19
           at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.InstallWorkloadsWithInstallRecord(IEnumerable`1 workloadIds, SdkFeatureBand sdkFeatureBand) in dotnet.dll:token 0x60001cf+0xb5
           at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.InstallWorkloads(IEnumerable`1 workloadIds, Boolean skipManifestUpdate) in dotnet.dll:token 0x60001ce+0x58
           at Microsoft.DotNet.Workloads.Workload.Install.WorkloadInstallCommand.Execute() in dotnet.dll:token 0x60001cb+0x216
           at Microsoft.DotNet.Cli.DotNetTopLevelCommandBase.RunCommand(String[] args) in dotnet.dll:token 0x60008af+0x5e
           at Microsoft.DotNet.Workloads.Workload.WorkloadCommand.Run(String[] args) in dotnet.dll:token 0x6000145+0x6
           at Microsoft.DotNet.Cli.Program.ProcessArgs(String[] args, TimeSpan startupTime, ITelemetry telemetryClient) in dotnet.dll:token 0x6000918+0x2e8
           at Microsoft.DotNet.Cli.Program.Main(String[] args) in dotnet.dll:token 0x6000916+0x6f
        • Sarah OslundMicrosoft employee

          Thanks for reporting this issue, you’re right. The original failure is the nuget package not being available on nuget.org but what ends up being surfaced here is a failure with the rollback. I’ve confirmed that with our current preview 5 builds this rollback issue has been fixed and the error reporting is more clear. In the meantime, you can solve this issue by including the necessary feed in a nuget.config:

          <?xml version="1.0" encoding="utf-8"?>
          <configuration>
            <packageSources>
              <add key="dotnet6" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet6/nuget/v3/index.json" />
            </packageSources>
          </configuration>
          
          • ThopDev

            Thanks adding this line to my nuget.config allowed me to install the package.

          • Daniel RothMicrosoft employee

            The missing packages should be available now on nuget.org. The NuGet.config workaround should no longer be necessary. Thanks for your patience on this!

  • Yaroslav Mudryk

    You have a mistake in next code example:

    public void ConfigureServices(IServiceCollection services)
    {
        services.AppHttpLogging();
    }
    

    Maybe you wanted to write:

    services.AddHttpLogging();

    ?

  • Steve Elliott

    When I went to run a new Maui Blazor App, it fails to find the NuGet packages. I’m receiving several errors like:

    Error NU1101 Unable to find package Microsoft.AspNetCore.Components.WebView.Maui. No packages exist with this id in source(s): Microsoft Visual Studio Offline Packages, nuget.org

    On a side note, after upgrading Visual Studio Preview to Version 16.11.0 Preview 1.0, it seems to have caused my Windows Terminal to not load. I haven’t tried restarting yet, but figured I’d mention it as a potential bug.

  • Riza Marhaban

    I’m very happy with this release and the progress. Awesome Daniel and to all of the .NET Team.

  • mu88

    Pretty interesting stuff – thank you for all your work 👍🏻 just a small question: when it comes to .NET MAUI Blazor apps and Windows resp. MacOS, is there any difference compared to Electron.NET? It seems pretty similar to me: a native executable with an embedded browser rendering Blazor components. Am I correct with that?

    • Daniel RothMicrosoft employee

      .NET MAUI Blazor apps do have a lot in common with Electron.NET. Both are hybrid app models, where you run natively on the device and then render web UI to an embedded browser. Electron.NET is a great open source project and definitely worth a look: https://github.com/ElectronNET/Electron.NET. I’m not an expert on Electron.NET, but as I understand things I believe some of the differences are:

      • Electron.NET is (not surprisingly) based on Electron. .NET MAUI has no dependency on Electron.
      • Electron.NET pairs an Electron app with a .NET & ASP.NET Core process. .NET MAUI Blazor apps run Blazor components natively in the .NET process and renders them to an embedded browser control using a local interop channel.
      • Electron depends on Chromium and Node. Electron.NET adds .NET into the mix. .NET MAUI leverages the browser stack from the underlying platform and is based on .NET 6.
      • Electron only targets desktop platforms. .NET MAUI targets desktop and mobile.
      • Electron supports Linux. .NET MAUI doesn’t have Linux support yet, but we expect to add it post .NET 6.
      • Electron is an older well-established framework with mainstream adoption and a strong ecosystem. .NET MAUI is newer and still in preview, although it has a strong heritage based on Xamarin.

      I may have missed some stuff here, so folks are welcome to comment if I left stuff out or misrepresented something.

      I hope this helps!

      • Mein Gott

        I don’t know why Electron.NET is still recommended for Blazor Desktop, when Chromely is much easy to implement and, above all, does not implement a node server. For the future I will continue to use Chromely or, depending on what version 2 of TryPhotino brings, on this one.

  • Paul Manzotti

    Are there any plans to produce a template for a SPA using Vue.js?

    • Max Fletcher

      It sounds like its going to be easier to set one of these up now, I have a feeling it will be easy to integrate Vue-CLI with the changes mentioned here.
      Chances are the community will create a template and post it on GitHub, but if not it might be something I do myself.

  • Ronan Rouyer

    Very nice release! A question about desktop applications, do you plan to have MAUI be the poster-child API to build them, or are WinForms, WPF and UWP still going to be supported in the foreseeable future?

    • Daniel RothMicrosoft employee

      Hi Ronan. .NET MAUI will be the preferred way to build xplat native apps with .NET 6, but WinForms, WPF, and UWP will continue to be supported.