{"id":37166,"date":"2021-10-12T10:01:37","date_gmt":"2021-10-12T17:01:37","guid":{"rendered":"https:\/\/devblogs.microsoft.com\/aspnet\/?p=25442"},"modified":"2021-11-01T09:54:00","modified_gmt":"2021-11-01T16:54:00","slug":"asp-net-core-updates-in-net-6-rc-2","status":"publish","type":"post","link":"https:\/\/devblogs.microsoft.com\/dotnet\/asp-net-core-updates-in-net-6-rc-2\/","title":{"rendered":"ASP.NET Core updates in .NET 6 Release Candidate 2"},"content":{"rendered":"<p><a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-6-release-candidate-2\/\">.NET 6 Release Candidate 2 (RC2) is now available<\/a>. .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 <a href=\"https:\/\/dotnetconf.net\">.NET Conf 2021<\/a>. It&#8217;s also a &#8220;go live&#8221; release, so you&#8217;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.<\/p>\n<p>Here&#8217;s what&#8217;s new in this preview release:<\/p>\n<ul>\n<li>Native dependencies support for Blazor WebAssembly apps<\/li>\n<li>Minimal API updates<\/li>\n<\/ul>\n<h2>Get started<\/h2>\n<p>To get started with ASP.NET Core in .NET 6 RC2, <a href=\"https:\/\/dotnet.microsoft.com\/download\/dotnet\/6.0\">install the .NET 6 SDK<\/a>.<\/p>\n<p>If you&#8217;re on Windows using Visual Studio, <a href=\"https:\/\/aka.ms\/vs2022preview\">install the latest preview of Visual Studio 2022<\/a>, which includes .NET 6 RC2. Mac users should <a href=\"https:\/\/visualstudio.microsoft.com\/vs\/mac\/preview\/\">install the latest preview of Visual Studio for Mac 2022<\/a>.<\/p>\n<h2>Upgrade an existing project<\/h2>\n<p>To upgrade an existing ASP.NET Core app from .NET 6 Preview RC1 to .NET 6 RC2:<\/p>\n<ul>\n<li>Update all Microsoft.AspNetCore.* package references to <code>6.0.0-rc.2.*<\/code>.<\/li>\n<li>Update all Microsoft.Extensions.* package references to <code>6.0.0-rc.2.*<\/code>.<\/li>\n<\/ul>\n<p>See the full list of <a href=\"https:\/\/docs.microsoft.com\/dotnet\/core\/compatibility\/6.0#aspnet-core\">breaking changes<\/a> in ASP.NET Core for .NET 6.<\/p>\n<h2>Native dependencies support for Blazor WebAssembly apps<\/h2>\n<p>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.<\/p>\n<p>To install the .NET WebAssembly build tools, select the optional component in the Visual Studio installer, or run <code>dotnet workload install wasm-tools<\/code> from an elevated command prompt. The .NET WebAssembly build tools are based on <a href=\"https:\/\/emscripten.org\/\">Emscripten<\/a>, a compiler toolchain for the web platform.<\/p>\n<p>You add native dependencies to your Blazor WebAssembly app by adding <code>NativeFileReference<\/code> items in your project file. When you build the project each <code>NativeFileReference<\/code> 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.<\/p>\n<p>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).<\/p>\n<h3>Using native code from a Blazor WebAssembly app<\/h3>\n<p>Let&#8217;s add a simple native C function to a Blazor WebAssembly app:<\/p>\n<ol>\n<li>Create a new Blazor WebAssembly project.<\/li>\n<li>Add a <em>Test.c<\/em> file to the project.<\/li>\n<li>Add a C function in <em>Test.c<\/em> for computing factorials:<\/li>\n<\/ol>\n<pre><code class=\"c\">int fact(int n)\r\n{\r\n    if (n == 0) return 1;\r\n    return n * fact(n - 1);\r\n}\r\n<\/code><\/pre>\n<ol>\n<li>Add a <code>NativeFileReference<\/code> for <em>Test.c<\/em> in your project file:<\/li>\n<\/ol>\n<pre><code class=\"xml\">&lt;ItemGroup&gt;\r\n  &lt;NativeFileReference Include=\"Test.c\" \/&gt;\r\n&lt;\/ItemGroup&gt;\r\n<\/code><\/pre>\n<ol>\n<li>In <em>Pages\/Index.razor<\/em> add a <code>DllImport<\/code> for the <code>fact<\/code> function in generated <em>Test<\/em> library:<\/li>\n<\/ol>\n<pre><code class=\"razor\">@using System.Runtime.InteropServices\r\n\r\n...\r\n\r\n@code {\r\n    [DllImport(\"Test\")]\r\n    static extern int fact(int n);\r\n}\r\n<\/code><\/pre>\n<ol>\n<li>Call <code>fact<\/code> method from your .NET code.<\/li>\n<\/ol>\n<pre><code class=\"razor\">&lt;p&gt;@fact(3)&lt;\/p&gt;\r\n<\/code><\/pre>\n<p>When you build the app with the .NET WebAssembly build tools installed, the native C code gets compiled and linked into <em>dotnet.wasm<\/em>. This may take a few minutes. Once the app is done building, run it to see the rendered factorial value.<\/p>\n<blockquote>\n<p>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.<\/p>\n<\/blockquote>\n<h3>Using libraries with native dependencies<\/h3>\n<p>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 <code>browser-wasm<\/code> <a href=\"https:\/\/docs.microsoft.com\/nuget\/create-packages\/supporting-multiple-target-frameworks#architecture-specific-folders\">architecture-specific folder<\/a>. WebAssembly-specific dependencies won&#8217;t get referenced automatically and need to be referenced manually as a <code>NativeFileReference<\/code>. Package authors can choose to add the native references by <a href=\"https:\/\/docs.microsoft.com\/nuget\/create-packages\/creating-a-package#include-msbuild-props-and-targets-in-a-package\">including a .props file in the package<\/a> with the references.<\/p>\n<p><a href=\"https:\/\/github.com\/mono\/SkiaSharp\">SkiaSharp<\/a> is a cross-platform 2D graphics library for .NET based on the native <a href=\"https:\/\/skia.org\/\">Skia<\/a> graphics library, and it now has preview support for Blazor WebAssembly. Let&#8217;s give it a try!<\/p>\n<p>To use SkiaSharp in a Blazor WebAssembly app:<\/p>\n<ol>\n<li>\n<p>Add a package reference to the SkiaSharp.Views.Blazor package from your Blazor WebAssembly project.<\/p>\n<p>dotnet add package &#8211;prerelease SkiaSharp.Views.Blazor<\/p>\n<\/li>\n<li>\n<p>Add a <code>SKCanvasView<\/code> component to your app.<\/p>\n<\/li>\n<\/ol>\n<pre><code class=\"razor\">&lt;SKCanvasView OnPaintSurface=\"OnPaintSurface\" \/&gt;\r\n<\/code><\/pre>\n<ol>\n<li>Add some drawing logic:<\/li>\n<\/ol>\n<pre><code class=\"razor\">@code {\r\n    void OnPaintSurface(SKPaintSurfaceEventArgs e)\r\n    {\r\n        var canvas = e.Surface.Canvas;\r\n        canvas.Clear(SKColors.White);\r\n        using var paint = new SKPaint\r\n        {\r\n            Color = SKColors.Black,\r\n            IsAntialias = true,\r\n            TextSize = 24\r\n        };\r\n        canvas.DrawText(\"SkiaSharp\", 0, 24, paint);\r\n    }\r\n}\r\n<\/code><\/pre>\n<ol>\n<li>Run the app to see your custom drawing with SkiaSharp!<\/li>\n<\/ol>\n<p><img decoding=\"async\" src=\"https:\/\/devblogs.microsoft.com\/aspnet\/wp-content\/uploads\/sites\/16\/2021\/10\/skiasharp.png\" alt=\"SkiaSharp\" \/><\/p>\n<p>You can find a complete <a href=\"https:\/\/github.com\/mono\/SkiaSharp\/tree\/main\/samples\/Basic\/Blazor\/WebAssembly\">sample for using SkiaSharp with Blazor WebAssembly<\/a> in the SkiaSharp repo.<\/p>\n<h2>Minimal API updates<\/h2>\n<h3>Parameter Binding<\/h3>\n<p>In RC2 we added support in <code>TryParse<\/code> and <code>BindAsync<\/code> for inherited methods. We also check for public <code>TryParse<\/code> and <code>BindAsync<\/code> methods that aren&#8217;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 <code>BindAsync<\/code> method now has another supported overload that doesn&#8217;t require the <code>ParameterInfo<\/code>: <code>public static ValueTask&lt;T?&gt; BindAsync(HttpContext context)<\/code>.<\/p>\n<p>You can add a static <code>TryParse<\/code> method on your custom type as shown below if you want to bind values from route, header attributes, and query strings. The <code>TryParse<\/code> method must be of the following forms:<\/p>\n<pre><code class=\"csharp\">public static bool TryParse(string value, T out result);\r\npublic static bool TryParse(string value, IFormatProvider provider, T out result);\r\n<\/code><\/pre>\n<p>Below is an example of the <code>TryParse<\/code> for a complex type <code>Point<\/code>:<\/p>\n<pre><code class=\"csharp\">app.MapGet(\"\/map\", (Point point) =&gt; $\"Point: {point.X}, {point.Y}\");\r\n\r\npublic class Point\r\n{\r\n    public double X { get; set; }\r\n    public double Y { get; set; }\r\n\r\n    public static bool TryParse(string? value, IFormatProvider? provider, out Point? point)\r\n    {\r\n        \/\/ Format is \"(12.3,10.1)\"\r\n        var trimmedValue = value?.TrimStart('(').TrimEnd(')');\r\n        var segments = trimmedValue?.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries);\r\n        if (segments?.Length == 2\r\n            &amp;&amp; double.TryParse(segments[0], out var x)\r\n            &amp;&amp; double.TryParse(segments[1], out var y))\r\n        {\r\n            point = new Point { X = x, Y = y };\r\n            return true;\r\n        }\r\n\r\n        point = null;\r\n        return false;\r\n    }\r\n} \r\n<\/code><\/pre>\n<p>The request <code>\/map?point=(10.1, 11.4)<\/code> to the above endpoint will return a <code>Point<\/code> object with the following property values <code>X=10.1, Y=11.4<\/code>.<\/p>\n<p>Furthermore, if you&#8217;d like to take control of the binding process for your type, you can use the following forms of <code>BindAsync<\/code>:<\/p>\n<pre><code class=\"csharp\">public static ValueTask&lt;T?&gt; BindAsync(HttpContext context, ParameterInfo? parameter);\r\npublic static ValueTask&lt;T?&gt; BindAsync(HttpContext context);\r\n<\/code><\/pre>\n<p>Want to bind a complex type using inheritance? <code>BindAsync<\/code> allows you to do it as demonstrated in the example below:<\/p>\n<pre><code class=\"csharp\">app.MapPost(\"\/widget\", (CreateWidgetDTO dto) =&gt;\r\n{\r\n    \/\/ Use the DTO\r\n});\r\n\r\npublic abstract class DTO&lt;T&gt;\r\n{\r\n    \/\/ typeof(T) must equal ParameterInfo.Type otherwise we throw\r\n    public static T BindAsync(HttpContext context, ParameterInfo parameter)\r\n    {\r\n        \/\/ Use reflection to bind the properties on T\r\n    }\r\n}\r\n\r\npublic class CreateWidgetDTO : DTO&lt;CreateWidgetDTO&gt;\r\n{\r\n    [FromRoute]\r\n    public string? Name { get; set; }\r\n\r\n    [FromQuery]\r\n    public int Id { get; set; }\r\n}\r\n<\/code><\/pre>\n<p>We also added support for optional custom parameter types with <code>BindAsync<\/code>, including nullable structs as shown below:<\/p>\n<pre><code class=\"csharp\">app.MapGet(\"\/webhook\", (CloudEventStruct? cloudEvent) =&gt;\r\n{\r\n    redis.Publish(cloudEvent);\r\n});\r\n\r\npublic struct CloudEventStruct\r\n{\r\n    public static async ValueTask&lt;CloudEventStruct?&gt; BindAsync(HttpContext context, ParameterInfo parameter)\r\n    {\r\n        return await CloudEventParser.ReadEventAsync(context, parameter.Name);\r\n    }\r\n}\r\n<\/code><\/pre>\n<h3>OpenAPI<\/h3>\n<p>We added enhancements to allow you to describe whether the request body is required or not using the <code>Accepts&lt;TRequest&gt;()<\/code> extension method or <code>[Consumes(typeof(Todo), \"application\/json\", IsRequired = false)]<\/code> attribute. The <code>Accepts<\/code> extension method and <code>Consumes<\/code> attribute allow you to express both the type <code>Todo<\/code> and the contentType <code>application\/json<\/code> for your generated open-api docs as indicated in the example below.<\/p>\n<pre><code class=\"csharp\">app.MapPost(\"\/todo\", async (HttpRequest request) =&gt;\r\n{\r\n    var todo =  await request.Body.ReadAsJsonAsync&lt;Todo&gt;();\r\n    return todo is Todo ? Results.Ok(todo) : Results.Ok();\r\n})\r\n.Accepts&lt;Todo&gt;(isRequired: false, contentType: \"application\/json\");\r\n\r\napp.MapPost(\"\/todo\", HandlePostTodo);\r\n[Consumes(typeof(Todo), \"application\/json\", IsRequired = false)]\r\nIResult HandlePostTodo(HttpRequest request)\r\n{\r\n    var todo =  await request.Body.ReadAsJsonAsync&lt;Todo&gt;();\r\n    return todo is Todo ? Results.Ok(todo) : Results.Ok();\r\n}\r\n<\/code><\/pre>\n<p>If you&#8217;d like to group related endpoints into different collections in OpenAPI docs (Swagger), you can do so using the <code>WithTags<\/code> extension method that allows you to provide the grouping tags metadata. See usage example below:<\/p>\n<pre><code class=\"csharp\">app.MapGet(\"\/\", () =&gt; \"Hello World!\")\r\n    .WithTags(\"Examples\");\r\n\r\napp.MapGet(\"\/todos\/sample\", () =&gt; new[]\r\n    {\r\n        new Todo { Id = 1, Title = \"Do this\" },\r\n        new Todo { Id = 2, Title = \"Do this too\" }\r\n    })\r\n    .WithTags(\"Examples\", \"TodoApi\");\r\n<\/code><\/pre>\n<h3>Source Code Analysis<\/h3>\n<p>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 <code>WebApplicationBuilder<\/code> and warn you when an incorrect middleware configuration or order is detected.<\/p>\n<p>We also added support to detect when a type that implements <code>IActionResult<\/code> is returned from the route handler and warn you about the unintentional serialization of the result as JSON. See example below:<\/p>\n<pre><code class=\"csharp\">app.Map(\"\/\", () =&gt; new JsonResult(new { Hello = \"World\" }));\r\n<\/code><\/pre>\n<p>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 :<\/p>\n<pre><code class=\"csharp\">app.Map(\"\/payment\", () =&gt; SubmitPayment());\r\n[Authorize]\r\nvoid SubmitPayment() { }  \r\n<\/code><\/pre>\n<p>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 <code>uuid<\/code> parameter as optional but it&#8217;s defined as required in the lambda function <code>(string uuid) =&gt; $\"{uuid}\"<\/code>.<\/p>\n<pre><code class=\"csharp\">app.MapGet(\"\/todo\/{uuid?}\", (string uuid) =&gt; $\"{uuid}\");\r\n<\/code><\/pre>\n<h3>Breaking Changes (APIs Renames)<\/h3>\n<p>We renamed the following APIs to provide clarity and properly describe their intents:<\/p>\n<ul>\n<li><code>DelegateEndpointConventionBuilder<\/code> -&gt; <code>RouteHandlerBuilder<\/code><\/li>\n<li><code>OpenApiDelegateEndpointConventionBuilderExtensions<\/code> -&gt; <code>OpenApiRouteHandlerBuilderExtensions<\/code><\/li>\n<li><code>DelegateEndpointRouteBuilderExtensions<\/code> merged with the existing <code>EndpointRouteBuilderExtensions<\/code>.<\/li>\n<\/ul>\n<p>The above changes replaced the <code>DelegateEndpoint<\/code> with <code>RouteHandler<\/code> and removed <code>Convention<\/code> in the class names.<\/p>\n<h2>Give feedback<\/h2>\n<p>We hope you enjoy this preview release of ASP.NET Core in .NET 6. We&#8217;re eager to hear about your experiences with this release. Let us know what you think by filing issues on <a href=\"https:\/\/github.com\/dotnet\/aspnetcore\/issues\">GitHub<\/a>.<\/p>\n<p>Thanks for trying out ASP.NET Core!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>.NET 6 Release Candidate 2 is now available! Check out all the improvements in ASP.NET Core in this update.<\/p>\n","protected":false},"author":417,"featured_media":21373,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"footnotes":""},"categories":[197,7509,7251],"tags":[],"class_list":["post-37166","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-aspnet","category-aspnetcore","category-blazor"],"acf":[],"blog_post_summary":"<p>.NET 6 Release Candidate 2 is now available! Check out all the improvements in ASP.NET Core in this update.<\/p>\n","_links":{"self":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/37166","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/users\/417"}],"replies":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/comments?post=37166"}],"version-history":[{"count":0,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/posts\/37166\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media\/21373"}],"wp:attachment":[{"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/media?parent=37166"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/categories?post=37166"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/devblogs.microsoft.com\/dotnet\/wp-json\/wp\/v2\/tags?post=37166"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}