ASP.NET Core updates in .NET 6 Release Candidate 2

Daniel

.NET 6 Release Candidate 2 (RC2) is now available. .NET 6 RC2 is very close to the final build of .NET 6 that we expect to ship in November this year in time for .NET Conf 2021. It’s also a “go live” release, so you’re welcome to use it in production. .NET 6 RC2 primarily contains quality improvements and bug fixes, although it does also include a few new features as well.

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

  • Native dependencies support for Blazor WebAssembly apps
  • Minimal API updates

Get started

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

If you’re on Windows using Visual Studio, install the latest preview of Visual Studio 2022, which includes .NET 6 RC2. Mac users should install the latest preview of Visual Studio for Mac 2022.

Upgrade an existing project

To upgrade an existing ASP.NET Core app from .NET 6 Preview RC1 to .NET 6 RC2:

  • Update all Microsoft.AspNetCore.* package references to 6.0.0-rc.2.*.
  • Update all Microsoft.Extensions.* package references to 6.0.0-rc.2.*.

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

Native dependencies support for Blazor WebAssembly apps

Blazor WebAssembly apps in .NET 6 can now use native dependencies that have been built to run on WebAssembly. You can statically link native dependencies into the .NET WebAssembly runtime using the .NET WebAssembly build tools, the same tools that you can use in .NET 6 to ahead-of-time (AOT) compile your Blazor app to WebAssembly or to relink the runtime to remove unused features.

To install the .NET WebAssembly build tools, select the optional component in the Visual Studio installer, or run dotnet workload install wasm-tools from an elevated command prompt. The .NET WebAssembly build tools are based on Emscripten, a compiler toolchain for the web platform.

You add native dependencies to your Blazor WebAssembly app by adding NativeFileReference items in your project file. When you build the project each NativeFileReference gets passed to Emscripten by the .NET WebAssembly build tools so that they are compiled and linked into the runtime. You can then p/invoke into the native code from your .NET code.

Generally any portable native code can be used as a native dependency with Blazor WebAssembly. You can add native dependencies to C/C++ code or code previously compiled using Emscripten: object files (.o), archive files (.a), bitcode (.bc), or standalone WebAssembly modules (.wasm). Prebuilt dependencies typically need to be built using the same version of Emscripten used to build the .NET WebAssembly runtime (currently 2.0.23).

Using native code from a Blazor WebAssembly app

Let’s add a simple native C function to a Blazor WebAssembly app:

  1. Create a new Blazor WebAssembly project.
  2. Add a Test.c file to the project.
  3. Add a C function in Test.c for computing factorials:
int fact(int n)
{
    if (n == 0) return 1;
    return n * fact(n - 1);
}
  1. Add a NativeFileReference for Test.c in your project file:
<ItemGroup>
  <NativeFileReference Include="Test.c" />
</ItemGroup>
  1. In Pages/Index.razor add a DllImport for the fact function in generated Test library:
@using System.Runtime.InteropServices

...

@code {
    [DllImport("Test")]
    static extern int fact(int n);
}
  1. Call fact method from your .NET code.
<p>@fact(3)</p>

When you build the app with the .NET WebAssembly build tools installed, the native C code gets compiled and linked into dotnet.wasm. This may take a few minutes. Once the app is done building, run it to see the rendered factorial value.

Note: You may get a build error on subsequent builds saying that the output assembly is being used by another process. This is a known issue that will be addressed for the .NET 6 release. To workaround the issue, rebuild the project again.

Using libraries with native dependencies

NuGet packages can contain native dependencies for use on WebAssembly. These libraries and their native functionality can then be used from any Blazor WebAssembly app. The files for the native dependencies should be built for WebAssembly and packaged in the browser-wasm architecture-specific folder. WebAssembly-specific dependencies won’t get referenced automatically and need to be referenced manually as a NativeFileReference. Package authors can choose to add the native references by including a .props file in the package with the references.

SkiaSharp is a cross-platform 2D graphics library for .NET based on the native Skia graphics library, and it now has preview support for Blazor WebAssembly. Let’s give it a try!

To use SkiaSharp in a Blazor WebAssembly app:

  1. Add a package reference to the SkiaSharp.Views.Blazor package from your Blazor WebAssembly project.

    dotnet add package –prerelease SkiaSharp.Views.Blazor

  2. Add a SKCanvasView component to your app.

<SKCanvasView OnPaintSurface="OnPaintSurface" />
  1. Add some drawing logic:
@code {
    void OnPaintSurface(SKPaintSurfaceEventArgs e)
    {
        var canvas = e.Surface.Canvas;
        canvas.Clear(SKColors.White);
        using var paint = new SKPaint
        {
            Color = SKColors.Black,
            IsAntialias = true,
            TextSize = 24
        };
        canvas.DrawText("SkiaSharp", 0, 24, paint);
    }
}
  1. Run the app to see your custom drawing with SkiaSharp!

SkiaSharp

You can find a complete sample for using SkiaSharp with Blazor WebAssembly in the SkiaSharp repo.

Minimal API updates

Parameter Binding

In RC2 we added support in TryParse and BindAsync for inherited methods. We also check for public TryParse and BindAsync methods that aren’t in the correct format and throw an error so you will know that you used the wrong syntax for your method(s). Additionally, the BindAsync method now has another supported overload that doesn’t require the ParameterInfo: public static ValueTask<T?> BindAsync(HttpContext context).

You can add a static TryParse method on your custom type as shown below if you want to bind values from route, header attributes, and query strings. The TryParse method must be of the following forms:

public static bool TryParse(string value, T out result);
public static bool TryParse(string value, IFormatProvider provider, T out result);

Below is an example of the TryParse for a complex type Point:

app.MapGet("/map", (Point point) => $"Point: {point.X}, {point.Y}");

public class Point
{
    public double X { get; set; }
    public double Y { get; set; }

    public static bool TryParse(string? value, IFormatProvider? provider, out Point? point)
    {
        // Format is "(12.3,10.1)"
        var trimmedValue = value?.TrimStart('(').TrimEnd(')');
        var segments = trimmedValue?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);
        if (segments?.Length == 2
            && double.TryParse(segments[0], out var x)
            && double.TryParse(segments[1], out var y))
        {
            point = new Point { X = x, Y = y };
            return true;
        }

        point = null;
        return false;
    }
} 

The request /map?point=(10.1, 11.4) to the above endpoint will return a Point object with the following property values X=10.1, Y=11.4.

Furthermore, if you’d like to take control of the binding process for your type, you can use the following forms of BindAsync:

public static ValueTask<T?> BindAsync(HttpContext context, ParameterInfo? parameter);
public static ValueTask<T?> BindAsync(HttpContext context);

Want to bind a complex type using inheritance? BindAsync allows you to do it as demonstrated in the example below:

app.MapPost("/widget", (CreateWidgetDTO dto) =>
{
    // Use the DTO
});

public abstract class DTO<T>
{
    // typeof(T) must equal ParameterInfo.Type otherwise we throw
    public static T BindAsync(HttpContext context, ParameterInfo parameter)
    {
        // Use reflection to bind the properties on T
    }
}

public class CreateWidgetDTO : DTO<CreateWidgetDTO>
{
    [FromRoute]
    public string? Name { get; set; }

    [FromQuery]
    public int Id { get; set; }
}

We also added support for optional custom parameter types with BindAsync, including nullable structs as shown below:

app.MapGet("/webhook", (CloudEventStruct? cloudEvent) =>
{
    redis.Publish(cloudEvent);
});

public struct CloudEventStruct
{
    public static async ValueTask<CloudEventStruct?> BindAsync(HttpContext context, ParameterInfo parameter)
    {
        return await CloudEventParser.ReadEventAsync(context, parameter.Name);
    }
}

OpenAPI

We added enhancements to allow you to describe whether the request body is required or not using the Accepts<TRequest>() extension method or [Consumes(typeof(Todo), "application/json", IsRequired = false)] attribute. The Accepts extension method and Consumes attribute allow you to express both the type Todo and the contentType application/json for your generated open-api docs as indicated in the example below.

app.MapPost("/todo", async (HttpRequest request) =>
{
    var todo =  await request.Body.ReadAsJsonAsync<Todo>();
    return todo is Todo ? Results.Ok(todo) : Results.Ok();
})
.Accepts<Todo>(isRequired: false, contentType: "application/json");

app.MapPost("/todo", HandlePostTodo);
[Consumes(typeof(Todo), "application/json", IsRequired = false)]
IResult HandlePostTodo(HttpRequest request)
{
    var todo =  await request.Body.ReadAsJsonAsync<Todo>();
    return todo is Todo ? Results.Ok(todo) : Results.Ok();
}

If you’d like to group related endpoints into different collections in OpenAPI docs (Swagger), you can do so using the WithTags extension method that allows you to provide the grouping tags metadata. See usage example below:

app.MapGet("/", () => "Hello World!")
    .WithTags("Examples");

app.MapGet("/todos/sample", () => new[]
    {
        new Todo { Id = 1, Title = "Do this" },
        new Todo { Id = 2, Title = "Do this too" }
    })
    .WithTags("Examples", "TodoApi");

Source Code Analysis

In RC2, we added a few analyzers to help you quickly find issues with your route handlers or warn you when there are misconfiguration issues for middleware. The analyzer will verify middleware ordering for WebApplicationBuilder and warn you when an incorrect middleware configuration or order is detected.

We also added support to detect when a type that implements IActionResult is returned from the route handler and warn you about the unintentional serialization of the result as JSON. See example below:

app.Map("/", () => new JsonResult(new { Hello = "World" }));

Furthermore, we introduced a new analyzer that will detect when attributes are put on a method called by a lambda instead of the lambda itself. for example, the following code will produce a warning :

app.Map("/payment", () => SubmitPayment());
[Authorize]
void SubmitPayment() { }  

Last but not least, for optional parameter binding, we added an analyzer that will throw an exception when there is a mismatched parameter optionality. Notice that the route string below defines the uuid parameter as optional but it’s defined as required in the lambda function (string uuid) => $"{uuid}".

app.MapGet("/todo/{uuid?}", (string uuid) => $"{uuid}");

Breaking Changes (APIs Renames)

We renamed the following APIs to provide clarity and properly describe their intents:

  • DelegateEndpointConventionBuilder -> RouteHandlerBuilder
  • OpenApiDelegateEndpointConventionBuilderExtensions -> OpenApiRouteHandlerBuilderExtensions
  • DelegateEndpointRouteBuilderExtensions merged with the existing EndpointRouteBuilderExtensions.

The above changes replaced the DelegateEndpoint with RouteHandler and removed Convention in the class names.

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!

32 comments

Leave a comment

  • Marc Huber

    Thanks for the update. Is the hot reloading feature for blazor wasm now available (for blazor server it is working, we have tested it)? If not, when will it be available?

      • Daniel RothMicrosoft employee

        Hi Carl. Have you created a Visual Studio Feedback issue for the Blazor WebAssembly debugging issue you are hitting? Debugging is working for me at least with the default Blazor WebAssembly template. It would be great if you could create an issue detailing the steps to reproduce the problem.

    • Daniel RothMicrosoft employee

      Hi Marc. Hot Reload should work in a limited set of scenarios with Blazor WebAssembly, but only when running without the debugger. With Blazor WebAssembly we basically only support method body replacement.

      • Carl Berisford-Murray

        So how does one go about this? I’ve tried dotnet watch, F5 from VS and CTRL-F5 from VS. The HTML/CSS doesn’t update, and I don’t believe that the code behind does either, but I can’t be 100% on that one.
        PS I’m using a hosted app, so when I launch it I launch the server.

        It was working with dotnet watch in about preview 4 or 5, but then stopped at 6 or 7. It was an awesome (but brief) bit of development bliss! Please tell me how to bring it back 🙏

        • Daniel RothMicrosoft employee

          Hi Carl. I’m sorry you’re still hitting issues with Hot Reload and Blazor WebAssembly! It should just work when you run the Blazor WebAssembly app from Visual Studio without the debugger. It should work with ASP.NET Core hosted Blazor WebAssembly apps too. Try it out with a new Blazor WebAssembly project created using the default .NET 6 template. Double check that .NET Hot Reload is enabled in the settings and that apply on file save is enabled or manually click the Hot Reload button to apply changes. If it’s not working even for a default Blazor WebAssembly project, send us a Visual Studio Feedback issue with some details about your environment. If the problem seems to be specific to your project, see if you can simplify the project to something we can reproduce and send us a Visual Studio Feedback issue with those details.

          • Lee Richardson

            I can’t get hot reload to work either in Blazor WebAssembly RC 2. Should I be setting hotReloadProfile to aspnetcore or blazorwasm in my launchSettings.json?

          • Daniel RothMicrosoft employee

            Hi Lee. Is Hot Reload not working for you even in a default new Blazor WebAssembly app in Visual Studio 2022 Preview 5? All the settings should be setup correctly in a new default project. If Hot Reload isn’t work, please create a Visual Studio Feedback issues using the Report a Problem feature. The hotReloadProfile should be blazorwasm in the project that you are running, even if it’s the host server project.

  • Mike-E

    Thank you for all your great work out there, team. The IDE/tooling/experience is getting so much better with each and every release. However, there are still some issues I am encountering.

    For instance, I have yet to get Hot Reload to work once, basically shrugging its shoulders after a long timeout:
    https://developercommunity.visualstudio.com/t/Exception-thrown-for-Hot-Reload-Notifica/1533134

    This still persists into Preview 5 and would be great to actually use at some point. 😁

    Thank you for any assistance/consideration you can provide. 🙏

    • Daniel RothMicrosoft employee

      Thanks Mike for reporting this issue and for all your feedback! I’m following up with the relevant engineering team to make sure this is getting the appropriate attention.

      • Mike-E

        Great! Thank you Daniel it is very much appreciated.

        FWIW Preview 5 seems like the best yet. The build problems I was experiencing in RC1 are now gone, and it is, in fact, FASTER (by about 25%!) than before the introduced churn of RC1. Additionally, the tooling seems to be holding incredibly well, having run the paces yesterday RAM utilization was hovering 3-4GB. This is down from 23GB at one point when first installing preview7.

        So, all rainbows and puppy dogs here. 🙂 Super props, recognition, and appreciation to you and your team for your lightning-fast care and attention to all our reported issues. 👍

  • Thorsten Sommer

    Thank you dear ASP.NET Core team and thank you to the community for putting so much work into ASP.NET Core. It’s really great what you folks have developed with Blazor in particular and ASP.NET Core in general. I really appreciate that we can now include native dependencies. You are doing valuable work that is appreciated by many developers. Thanks for everything.

    • Ibrahim Mohammed

      App service supports asp.net core 6 RC2 and all earlier preview releases. You may set to support preview releases from app service settings in Azure or select self-contained during publishing.

    • Byron TardifMicrosoft employee

      Hello @David App Service was updated to support .NET 6 RC2 on 11/14 across both Windows and Linux. This is being done through the Early Access mechanism (see App Service Early Access)

      If you had an app already configured to use .NET 6 with one of the previews, and your are not seeing .NET 6 RC2 you may need to restart the app so that the update can be picked up.

  • Marcin Swistak

    After upgrading to from .Net 5 to .Net 6 RC2, Windows authentication no longer works. I also tried created new project from template (ASP.NET Core Web App) with Windows Authentication, this doesnt work when launched it shows “Hello, !”. When do the same with .Net 5 it starts with “Hello, MyDomain\Marcin!”

  • Rod Macdonald

    Very interesting to see support for SkiaSharp.

    I’m looking forward to MS getting behind its own web/Windows vectorisable UI that runs in web and Windows. I’d discard non-UI XAML.

    I love WPF’s design surface, and it would be so nice to draw and render, draw and render… and then to code behind to run everywhere.

  • Radu Vacaru

    Hi, this is incredibly useful!

    What I am not sure it is supported, is isolating the C/C++ code in a .NET6 library, which produces a NuGet package including the .wasm compiled code.
    We would like to avoid including the actual C/C++ code in each of our consumers (Blazor WASM projects); we would rather ‘compile and pack’ once and reuse.
    I didn’t manage yet to make use of NativeFileReference in a ‘regular’ .NET6 library, the Emscripten toolchain does not ‘kick-in’ 🙂
    Is this supported?

    TIA!

      • Radu Vacaru

        OK, thanks!

        Are the .cpp/.h pairs supported somehow via NativeFileReference?

        For example:

        ———-test.h
        namespace Calculator
        {
        class Simple abstract sealed {
        public:
        static double Add(double one, double two);
        };
        }

        ———-test.cpp

        include “test.h”

        namespace Calculator
        {
        double Simple::Add(double one, double two)
        {
        return one + two;
        }
        }

  • Luis Harvey Triana Vega

    Thanks. Excellent and amazing progress in Blazor. Support for native dependencies has caught my attention. I have experimented with C++ code, the functions with structures works very fine. However, I wanted to go ahead and experiment with C++ callbacks to Blazor. When testing callbacks from C++ to Blazor code, I get this exception:

    Assertion at /__w/1/s/src/mono/mono/metadata/loader.c:1806, condition '<disabled>' not met
    

    How to submit the samples for team evaluation?

  • Wade Balzer

    Creating a new WASM Core Hosted PWA with Individual Accounts, I noticed that after applying Migrations and logging in, I am getting an error…

    Refused to load the script 'https://localhost:7139/_framework/aspnetcore-browser-refresh.js' because it violates the following Content Security Policy directive: "script-src 'sha256-fa5rxHhZ799izGRP38+h4ud5QXNT0SFaFlh4eqDumBI='". Note that 'script-src-elem' was not explicitly set, so 'script-src' is used as a fallback.

    I am also getting…

    blazor.webassembly.js:1 TypeError: Failed to fetch
        at a (blazor.webassembly.js:1)
        at Function.initAsync (blazor.webassembly.js:1)
        at Ct (blazor.webassembly.js:1)
        at blazor.webassembly.js:1
        at blazor.webassembly.js:1

    I don’t have any visible errors on the page, but I noticed that JavaScript inside a SCRIPT tag on a page, is also refusing to run because of a Content Security Policy. I’m not sure if this policy is new to the browser, or if the policy is new because of our Corporate Group Policies. I have tried to follow the instructions on:

    https://docs.microsoft.com/en-us/aspnet/core/blazor/security/content-security-policy?view=aspnetcore-6.0

    I wasn’t able to figure out how to set the meta tag to prevent the error and allow JS constants to be written inside a SCRIPT tag that may change from a configuration in the launch settings. Any ideas?

  • Manoj Kumar

    Thank you all for the great work.
    App released in Aot mode taking a lot of memory, with .Net 6 rc2 it is taking around 200 MB, with AOT it is going up to 2GB on start up itself and not coming down
    is there any settings or fix to reduce memory usage?