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

Daniel

.NET 6 Release Candidate 1 (RC1) is now available and includes many great new improvements to ASP.NET Core.

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

  • Render Blazor components from JavaScript
  • Blazor custom elements
  • Manipulate the query string from Blazor
  • .NET to JavaScript streaming
  • PageX and PageY in MouseEventArgs
  • Blazor templates updated to set page titles
  • Disabled long-polling transport for Blazor Server
  • Collocate JavaScript files with pages, views, and components
  • JavaScript initializers
  • Customize Blazor WebAssembly packaging
  • Template improvements
  • Minimal API updates
  • Support for Latin1 encoded request headers in HttpSysServer
  • Emit KestrelServerOptions via EventSource event
  • Add timestamps and PID to ASP.NET Core Module logs
  • New DiagnosticSource event for rejected HTTP requests
  • Create a ConnectionContext from an Accept Socket
  • Streamlined HTTP/3 setup
  • Upgrade to Duende Identity Server

Get started

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

If you’re on Windows using Visual Studio, install the latest preview of Visual Studio 2022. Support for .NET 6 in Visual Studio for Mac is coming soon, and is currently available as a private preview.

To get setup with .NET MAUI & Blazor for cross-platform native apps, see the latest instructions in the .NET MAUI getting started guide. Please also read this important update on .NET MAUI.

To install the .NET WebAssembly build tools, run the following command from an elevated command prompt:

dotnet workload install wasm-tools

Alternative, use the Visual Studio Installer to enable the “.NET WebAssembly build tools” optional component in the “ASP.NET and web development” workload.

Upgrade an existing project

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

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

To upgrade a .NET MAUI Blazor app from .NET 6 Preview 7 to .NET 6 RC1 we recommend starting from a new .NET MAUI Blazor project created with the .NET 6 RC1 SDK and then copying code over from your original project.

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

Render Blazor components from JavaScript

Blazor is great for building client-side web UI using just .NET, but what if you already have existing JavaScript apps that you need to maintain and evolve? How can you avoid having to build common components twice, in .NET and JavaScript?

In .NET 6, you can now dynamically render Blazor components from JavaScript. This capability enables you to integrate Blazor components with existing JavaScript apps.

To render a Blazor component from JavaScript, first register it as a root component for JavaScript rendering and assign it an identifier:

Blazor Server

builder.Services.AddServerSideBlazor(options =>
{
    options.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");
});

Blazor WebAssembly

builder.RootComponents.RegisterForJavaScript<Counter>(identifier: "counter");

Load Blazor into your JavaScript app (blazor.server.js or blazor.webassembly.js) and then render the component from JavaScript into a container element using the registered identifier, passing component parameters as needed:

let containerElement = document.getElementById('my-counter');
await Blazor.rootComponents.add(containerElement, 'counter', { incrementAmount: 10 });

Blazor custom elements

Experimental support is also now available for building custom elements with Blazor using the Microsoft.AspNetCore.Components.CustomElements NuGet package. Custom elements use standard HTML interfaces to implement custom HTML elements.

To create a custom element using Blazor, register a Blazor root component as custom elements like this:

options.RootComponents.RegisterAsCustomElement<Counter>("my-counter");

You can then use this custom element with any other web framework you’d like. For example, here’s how you would use this Blazor counter custom element in a React app:

<my-counter increment-amount={incrementAmount}></my-counter>

See the Blazor Custom Elements sample project for a complete example of how to create custom elements with Blazor.

This feature is experimental because we’re still working out some of the details for how best to support custom elements with Blazor. We welcome your feedback on how well this particular approach meets your requirements.

Generate Angular and React components using Blazor

You can also now generate framework specific JavaScript components from Blazor components for frameworks like Angular or React. This capability isn’t included with .NET 6, but is enabled by the new support for rendering Blazor components from JavaScript. The JavaScript component generation sample on GitHub demonstrates how you can generate Angular and React components from Blazor components.

In this sample, you attribute Blazor components to generate Angular or React component wrappers:

@*Generate an Angular component*@
@attribute [GenerateAngular]

@*Generate an React component*@
@attribute [GenerateReact]

You then register the Blazor components as Angular or React components:

options.RootComponents.RegisterForAngular<Counter>();
options.RootComponents.RegisterForReact<Counter>();

When the project gets built, it generates Angular and React components based on your Blazor components. You then use the generated Angular and React components like you would normally:

// Angular
<counter [incrementAmount]="incrementAmount"></counter>
// React
<Counter incrementAmount={incrementAmount}></Counter>

This sample isn’t a complete solution for generating Angular and React components from Blazor components, but we hope it demonstrates what’s possible. We welcome and encourage community efforts to build on this functionality more fully. We’re excited to see what the community does with this feature!

Manipulate the query string from Blazor

New GetUriWithQueryParameter and GetUriWithQueryParameters extension methods on NavigationManager facilitate updating the query string of the browser URL.

To add or update a single query string parameter:

// Create a new URI based on the current address
// with the specified query string parameter added or updated.
var newUri = NavigationManager.GetUriWithQueryParameter("page", 3);

// Navigate to the new URI with the updated query string
NavigationManager.NavigateTo(newUri);

Use GetUriWithQueryParameters to add, update, or remove multiple parameters based on a dictionary of parameter values (a null value removes the parameter).

.NET to JavaScript streaming

Blazor now supports streaming data from .NET to JavaScript. A .NET stream can be passed to JavaScript as a DotNetStreamReference.

using var data = new System.IO.MemoryStream(new byte[100 * 1024]);
using var streamRef = new DotNetStreamReference(stream: data, leaveOpen: false);
await JS.InvokeVoidAsync("consumeStream", streamRef);

In JavaScript, the data stream can then be read as an array buffer or as a readable stream:

async function consumeStream(streamRef) {
    const data = await streamRef.arrayBuffer();    // ArrayBuffer
    // or
    const stream = await streamRef.stream();       // ReadableStream
}

Additional details on this feature are available in the Blazor JavaScript interop docs.

PageX and PageY in MouseEventArgs

MouseEventArgs now has PageX and PageY properties corresponding to the standard pagex and pagey on the MouseEvent interface. Thank you TonyLugg for helping us fill this functional gap.

Blazor templates updated to set page title

The Blazor project templates have been updated to support updating the page title as the user navigates to different pages using the new PageTitle and HeadOutlet components.

In the Blazor WebAssembly template, the HeadOutlet component is added as a root component that appends to the HTML head tag. Each page in the template sets the title using the PageTitle component.

The Blazor Server template required a bit more refactoring in order to support modifying the head when prerendering. The _Host.cshtml page now has its own layout, _Layout.cshtml, that adds the HeadOutlet component to the HTML head using the component tag helper. This ensures that the HeadOutlet is rendered before any components that want to modify the head.

Disabled long-polling transport for Blazor Server

Prior to .NET 6, Blazor Server apps would fall back to long-polling when WebSockets weren’t available, which often led to a degraded user experience. In .NET 6 we’ve disabled the long-polling transport for Blazor Server apps by default so that it’s easier to know when WebSockets haven’t been correctly configured. If your Blazor Server app still requires support for long-polling, you can reenable it. See the related breaking change notification for details.

Collocate JavaScript files with pages, views, and components

You can now collocate a JavaScript file with pages, views, and components using the .cshtml.js and .razor.js conventions. This is a convenient way to organize your code when you have JavaScript code that is specific to a page, view, or component. These files are publicly addressable using the path to the file in the project (Pages/Index.cshtml.js or _content/{LIBRARY NAME}/Pages/Index.cshtml.js if the file is coming from a library).

For example, if you have a component implemented by Pages/Counter.razor, you can add a JavaScript module for that component at Pages/Counter.razor.js and then load it like this:

var module = await JS.InvokeAsync<IJSObjectReference>("import", "./Pages/Counter.razor.js");

JavaScript initializers

JavaScript initializers provide a way to execute some logic before and after a Blazor app loads. This is useful for customizing how a Blazor app loads, initializing libraries before Blazor starts up, and configuring Blazor settings.

To define a JavaScript initializer, add a JavaScript module to the web root of your project named {LIBRARY NAME}.lib.module.js. Your module can export the following well-known functions:

  • beforeStart: Called before Blazor boots up on the .NET side. Used to customize the loading process, logging level, and other hosting model specific options.
    • In Blazor WebAssembly, beforeStart receives the Blazor WebAssembly options and any extensions added during publishing.
    • In Blazor Server, beforeStart receives the circuit start options.
    • In BlazorWebViews, no options are passed.
  • afterStarted: Called after Blazor is ready to receive calls from JavaScript. Used to initialize libraries by making .NET interop calls, registering custom elements, etc.
    • The Blazor instance is always passed to afterStarted as an argument.

A basic JavaScript initializer looks like this:

RazorClassLibrary1.lib.module.js

export function beforeStart(options) {
    console.log("beforeStart");
}
export function afterStarted(blazor) {
    console.log("afterStarted");
}

JavaScript initializers are detected as part of the build and then imported automatically in Blazor apps. This removes the need in many cases for manually adding script references when using Blazor libraries.

Customize Blazor WebAssembly packaging

In some environments, firewalls or other security software block the download of .NET assemblies, which prevents the execution of Blazor WebAssembly apps. To enable support for Blazor WebAssembly in these environments, we’ve made the publishing and loading process for Blazor WebAssembly apps extensible so that you can customize the packaging and loading of the app. We’ll share more details on how to do this in a future post.

Template improvements

Implicit usings

Based on feedback from Preview 7, changes to the implicit usings feature were made as part of this release, including the requirement to opt-in to implicit usings in the project file, rather than them being included by default based on the project targeting .NET 6. This will ensure existing projects being migrated to .NET 6 aren’t impacted by the implicit usings until the author is ready to enable the feature.

You can read more about this change in its breaking changes document.

Randomized port allocation

New ASP.NET Core projects will now have random ports assigned during project creation for use by the Kestrel web server, matching the existing behavior when using IIS Express. This helps to minimize the chance that new projects end up using ports that are already in use on the machine, which results in a failure of the app to start when it’s launched from Visual Studio, or via dotnet run.

A port from 5000-5300 will be selected for HTTP, and from 7000-7300 for HTTPS, at the time the project is created. As always, the ports used during development can be easily changed by editing the project’s launchSettings.json file. When the app is run after publishing, Kestrel still defaults to using ports 5000 and 5001 (for HTTP and HTTPS respectively) if not otherwise configured. You can read more about configuring Kestrel in the docs.

New logging defaults

New ASP.NET Core projects have a simplified logging configuration in their appsettings.json and appsettings.Development.json files.

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  }
}

The net effect of this change is that log messages at the “Information” level from sources other than ASP.NET Core, will now be emitted by default. This includes messages related to relational database querying from Entity Framework Core, which are now clearly visible by default in new applications:

info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:7297
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5131
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\Users\MyName\source\repos\WebApplication45\WebApplication45
info: Microsoft.EntityFrameworkCore.Database.Command[20101]
      Executed DbCommand (25ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
      SELECT [w].[Id], [w].[Name]
      FROM [Widget] AS [w]

Minimal API updates

Improved support for OpenAPI

In RC1, we improved support for OpenAPI by adding ways to define metadata on a minimal endpoint either imperatively via extensions methods or declaratively via attributes.

We added support for the following metadata:

  • WithName metadata maps the endpoint name to an operationId in generated OpenAPI documents.
    • Note the endpoint name is also used when using LinkGenerator to generate URL/links for endpoints
    • When the user does not specify the endpoint name using the WithName metadata, the operation id will default to the name of the function (SayHello) as shown in the following example:
string SayHello(string name) => $"Hello, {name}!";
app.MapGet("/hello/{name}", SayHello);
  • WithGroupName metadata maps the endpoint group name to the document name in generated OpenAPI documents (typically used for versioning, e.g. “v1”, “v2”)
  • ExcludeFromDescription metadata indicates that the API should be excluded/ignored from OpenAPI document generation
  • ProducesValidationProblem indicates that the endpoint will produce 4xx http status codes and error details with application/validationproblem+json content type
  • Produces<T>(...) metadata indicates what response types a method produces, where each response is the combination of:
    • One HTTP status code
    • One or more content types, e.g. “application/json”
    • An optional schema per content type

Examples

OpenAPI extension methods can be used to imperatively add the required metadata to the endpoint:

app.MapGet("/admin", () => "For admins only")
   .WithName("AdminDetails")
   .RequiresAuthorization("Admins")
   .ExcludeFromDescription();

app.MapPost("/todos", async (Todo todo, TodoDb db) =>
    {
        db.Todos.Add(todo);
        await db.SaveChangesAsync();

        return Results.CreatedAtRoute("GetTodoById", new { todo.Id }, todo);
    })
    .WithName("AddTodo")
    .WithGroupName("v1")
    .ProducesValidationProblem()
    .Produces<Todo>(StatusCodes.Status201Created);

In addition to extension methods, attributes can be used to add metadata declaratively on endpoints:

app.MapGet("/admin", AdminDetails);
app.MapPost("/todos", AddTodo);

[Authorized(Policy = "Admins")]
[ExcludeFromDescription]
string AdminDetails()
{
    return "For admins only";
}

[EndpointGroupName("v1")]
[ProducesResponseType(typeof(ValidationProblemDetails), StatusCodes.Status400BadRequest, "application/problem+json")]
[ProducesResponseType(typeof(Todo), StatusCodes.Status201Created)]
async Task<IResult> AddTodo(Todo todo, TodoDb db)
{
    db.Todos.Add(todo);
    await db.SaveChangesAsync();

    return Results.Created($"/todos/{todo.Id}", todo);
}

Parameter Binding improvements

Allow optional parameters in endpoint actions

We introduced support to allow developers to specify if parameters in their minimal action are optional.

For example, we can designate name as an optional route parameter in the sample below:

app.MapGet("/sayHello/{name?}", (string? name) => $"Hello World from {name}");

This indicates that both of the following calls will succeed by returning 2xx status code since the name parameter is optional.
curl localhost:5000/sayHello/John and curl localhost:5000/sayHello.

The same behavior applies for request bodies. The following example will call the method with a null todo when there is no a request body sent in a post/put call:

app.MapPost("/todos", (Todo? todo) => () => { });

Route parameters, query parameters, and body parameters can all be designated as optional by using a nullability annotation or providing a default value. When a required parameter is not provided, the delegate will return a 400 status.

As part of the changes, we also improved error messages when parameters fail to bind.

Fix issue with struct support in parameter binding

In Preview 7, binding a struct parameter did not work and would throw an System.InvalidOperationException: The binary operator Equal is not defined for the types 'Contact' and 'System.Object'. In RC1, we fixed the issue.

Now it’s possible for developers to write the code below for a minimal API:

var app = WebApplication.Create(args);

app.MapPost("/api/contact", (Contact contact) => $"{contact.PhoneNumber} {contact.Email}");

app.Run();

record struct Contact(string PhoneNumber, string Email);

Developer Exception Page Middleware change

With RC1, the DeveloperExceptionPageMiddleware will now be registered as the first middleware if the current environment is development. This means when IWebHostEnvironment.IsDevelopment() is true. This removes the need to manually register the middleware by developers as shown in the example below.

Before

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

if (app.Environment.IsDevelopment())
{
    app.UseDeveloperExceptionPage();
}
app.MapGet("/", "Hello World");

app.Run();

After

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

app.MapGet("/", "Hello World");

app.Run();

Other Improvements in minimal APIs

Modifying the host configuration after the WebApplicationBuilder has been created is no longer supported. Instead, the Create and CreateBuilder calls now support taking a WebApplicationBuilderOptions which can be used to specify properties like the environment name, content root path, and so on.

var config = new WebApplicationOptions 
{
    Args = args,
    EnvironmentName = Environments.Staging,
    ContentRootPath = "www/"
};

var app = WebApplication.Create(options);

app.MapGet("/", (IHostEnvironment env) => env.EnvironmentName);

app.Run();

In addition to configuring hosting settings using the WebApplicationOptions class, minimal APIs now support customizing the host configuration using command-line args. For example, the following can be used to set the environment name for a running app.

$ dotnet run --environment=Development

To support a wider variety of middleware, minimal API apps now support multiple calls to the UseRouting method without overriding existing endpoints. This enables registering middleware like the Developer Exception page (which is enabled by default now) in the app.

Minimal APIs now support using MapFallback to define behavior for fallback routes in apps.

var app = WebApplication.Create(options);

app.MapFallback("/subroute", (string shareCode) => { ... };

app.Run();

Support for Latin1 encoded request headers in HttpSysServer

HttpSysServer is now capable of decoding request headers that are Latin1 encoded. To do so, you must the set the UseLatin1RequestHeaders property on HttpSysOptions to true.

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.UseHttpSys(o => o.UseLatin1RequestHeaders = true);

Emit KestrelServerOptions via EventSource event

The KestrelEventSource now emits a new event containing the JSON-serialized KestrelServerOptions when enabled (with verbosity EventLevel.LogAlways). This event makes it easier to reason about the server behavior when analyzing collected traces. Here’s an example of the event payload:

{
  "AllowSynchronousIO": false,
  "AddServerHeader": true,
  "AllowAlternateSchemes": false,
  "AllowResponseHeaderCompression": true,
  "EnableAltSvc": false,
  "IsDevCertLoaded": true,
  "RequestHeaderEncodingSelector": "default",
  "ResponseHeaderEncodingSelector": "default",
  "Limits": {
    "KeepAliveTimeout": "00:02:10",
    "MaxConcurrentConnections": null,
    "MaxConcurrentUpgradedConnections": null,
    "MaxRequestBodySize": 30000000,
    "MaxRequestBufferSize": 1048576,
    "MaxRequestHeaderCount": 100,
    "MaxRequestHeadersTotalSize": 32768,
    "MaxRequestLineSize": 8192,
    "MaxResponseBufferSize": 65536,
    "MinRequestBodyDataRate": "Bytes per second: 240, Grace Period: 00:00:05",
    "MinResponseDataRate": "Bytes per second: 240, Grace Period: 00:00:05",
    "RequestHeadersTimeout": "00:00:30",
    "Http2": {
      "MaxStreamsPerConnection": 100,
      "HeaderTableSize": 4096,
      "MaxFrameSize": 16384,
      "MaxRequestHeaderFieldSize": 16384,
      "InitialConnectionWindowSize": 131072,
      "InitialStreamWindowSize": 98304,
      "KeepAlivePingDelay": "10675199.02:48:05.4775807",
      "KeepAlivePingTimeout": "00:00:20"
    },
    "Http3": {
      "HeaderTableSize": 0,
      "MaxRequestHeaderFieldSize": 16384
    }
  },
  "ListenOptions": [
    {
      "Address": "https://127.0.0.1:7030",
      "IsTls": true,
      "Protocols": "Http1AndHttp2"
    },
    {
      "Address": "https://[::1]:7030",
      "IsTls": true,
      "Protocols": "Http1AndHttp2"
    },
    {
      "Address": "http://127.0.0.1:5030",
      "IsTls": false,
      "Protocols": "Http1AndHttp2"
    },
    {
      "Address": "http://[::1]:5030",
      "IsTls": false,
      "Protocols": "Http1AndHttp2"
    }
  ]
}

Add timestamps and PID to ASP.NET Core Module logs

The ASP.NET Core Module (ANCM) enhanced diagnostic logs now include timestamps and PID of the process emitting the logs. This makes it easier to diagnose issues with overlapping process restarts in IIS when you may have multiple IIS worker processes running.

The resulting logs now resemble the sample output included below:

[2021-07-28T19:23:44.076Z, PID: 11020] [aspnetcorev2.dll] Initializing logs for 'C:\<path>\aspnetcorev2.dll'. Process Id: 11020. File Version: 16.0.21209.0. Description: IIS ASP.NET Core Module V2. Commit: 96475a2acdf50d7599ba8e96583fa73efbe27912.
[2021-07-28T19:23:44.079Z, PID: 11020] [aspnetcorev2.dll] Resolving hostfxr parameters for application: '.\InProcessWebSite.exe' arguments: '' path: 'C:\Temp\e86ac4e9ced24bb6bacf1a9415e70753\'
[2021-07-28T19:23:44.080Z, PID: 11020] [aspnetcorev2.dll] Known dotnet.exe location: ''

New DiagnosticSource event for rejected HTTP requests

Kestrel now emits a new DiagnosticSource event for HTTP requests rejected at the server layer. Prior to this change, there was no way to observe these rejected requests. The new DiagnosticSource event Microsoft.AspNetCore.Server.Kestrel.BadRequest now contains a IBadRequestExceptionFeature that can be used to introspect the reason for rejecting the request.

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();
var diagnosticSource = app.Services.GetRequiredService<DiagnosticListener>();
using var badRequestListener = new BadRequestEventListener(diagnosticSource, (badRequestExceptionFeature) =>
{
    app.Logger.LogError(badRequestExceptionFeature.Error, "Bad request received");
});
app.MapGet("/", () => "Hello world");

app.Run();

class BadRequestEventListener : IObserver<KeyValuePair<string, object>>, IDisposable
{
    private readonly IDisposable _subscription;
    private readonly Action<IBadRequestExceptionFeature> _callback;

    public BadRequestEventListener(DiagnosticListener diagnosticListener, Action<IBadRequestExceptionFeature> callback)
    {
        _subscription = diagnosticListener.Subscribe(this!, IsEnabled);
        _callback = callback;
    }
    private static readonly Predicate<string> IsEnabled = (provider) => provider switch
    {
        "Microsoft.AspNetCore.Server.Kestrel.BadRequest" => true,
        _ => false
    };
    public void OnNext(KeyValuePair<string, object> pair)
    {
        if (pair.Value is IFeatureCollection featureCollection)
        {
            var badRequestFeature = featureCollection.Get<IBadRequestExceptionFeature>();

            if (badRequestFeature is not null)
            {
                _callback(badRequestFeature);
            }
        }
    }
    public void OnError(Exception error) { }
    public void OnCompleted() { }
    public virtual void Dispose() => _subscription.Dispose();
}

Create a ConnectionContext from an Accept Socket

The newly introduced SocketConnectionContextFactory now makes it possible to create a ConnectionContext from an already accepted socket. This makes it possible to build a custom Socket-based IConnectionListenerFactory without losing out on all the performance work and pooling happening in SocketConnection.

Look at this example of a custom IConnectionListenerFactory for an example of how to use this new API.

Streamlined HTTP/3 setup

RC1 introduces an easier setup experience for using HTTP/3 in Kestrel. All that’s needed is to configure Kestrel to use the proper protocol.

HTTP/3 can be enabled on all ports using ConfigureEndpointDefaults, or for an individual port, as in the sample below.

var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel((context, options) =>
{
    options.Listen(IPAddress.Any, 5001, listenOptions =>
    {
        // Use HTTP/3
        listenOptions.Protocols = HttpProtocols.Http1AndHttp2AndHttp3;
        listenOptions.UseHttps();
    });
});

HTTP/3 is not supported everywhere. See Use HTTP/3 with the ASP.NET Core Kestrel web server for information on getting started with HTTP/3 in Kestrel.

Upgrade to Duende Identity Server

Templates which use Identity Server have now be updated to use Duende Identity Server, as previously discussed in our announcement.

If you are extending the identity models and are updating existing projects you will need to update the namespaces in your code from IdentityServer4.IdentityServer to Duende.IdentityServer and follow their migration instructions.

Please note the license model for Duende Identity Server has changed to a reciprocal license, which may require license fees if you use it commercially in production. You can check the Duende license page for more details.

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!

61 comments

Leave a comment

  • Mike-E

    Woohoooo thank you so much for all your efforts over there, Mr. Roth & Team! I have been trying out RC1 for the past couple of weeks and it has been soooooooo much better than preview7 and prior. Most of the major memory/tooling issues have been addressed and my VS2022 instance RAM utilization hovers around 4-8GB of usage these days. This is down from 23-24GB with all the memory leak issues in prior Razor tooling… that were also occurring in VS2019. 😅😭

    The only concern I have now is that in any RC1 build that I have tried later than 6.0.100-rc.1.21416.1, build times seem to be 25%-50% longer. I fear that it may be Blazor-related but not sure. It also seems that the larger the solution, the more pronounced it gets. Any resources you can provide to tracking this down would be greatly appreciated:
    https://developercommunity.visualstudio.com/t/Increased-Build-Times-in-net60-RC2-over/1517237

    In any case, I am so appreciative of all the efforts thus far you have made to improve the tooling. It is SO much better now and much closer to a stable, final product. Please do continue the great work out there. 👍

    • Mike-E

      WOW that issue is already Under Investigation. Thank you for your attention and diligence in this matter. 🙏

      The only other issue. 😇 If you are using Syncfusion Blazor controls, there is a problem with upgrading between preview7 to RC1. RC1 breaks the SfGrid control. This is a breaking runtime change and has been reported, but the issue has been closed “by designed” (EDIT: It’s been reopened… we’re on a roll here!):

      https://github.com/dotnet/runtime/issues/58469

  • Afshin Z

    It would be great if Kestrel could handle multiple websites on one server. Currently I have to use nginx to determine which website the request is for and then pass it to the correct Kestrel instance.

      • Afshin Z

        So for example I have a server with one IP address and I want to host two ASP.NET Core websites on this server. Both websites will use port 443 but one is accessible via alpha.com and the other via beta.com domain. Kestrel can only handle one website on a port. So the solution for me was to use nginx which can handle multiple websites on one port and then pass the request to the appropriate Kestrel instance, say one on localhost:5000 and the other on localhost:5001.

        I hope I was clear.

  • Bob Millar

    When upgrading an existing project using Minimal APIs add the following new items to the PropertyGroup element of the Project File:

    “`
    <ImplicitUsings>enable</ImplicitUsings>
    <RootNamespace>xxxxx</RootNamespace>

    “`

  • Phillip Munn

    We’re a company a developing a MVC Web Application with Server Side Blazor components that has approximately 80 controllers and associated .cshtml views.
    Some of our more complicated functionality allows employees to place themselves on a “rota” for work at a specific time of day and we have written this sections of the website using Blazor Server Side technology rather than using traditional MVC pages in a “hybrid approach”. This means employees can be scheduled to work and we can update the associated costs – e.g. the estimated payroll costs in realtime using these latest technologies.

    We’ve been really looking forward to Hot Reload/Net 6.0 to solve our biggest pain point which is currently that editing any of the .razor files in the Blazor Server Side components would not show changes until the entire web app was reloaded. This makes even fixing simple spelling mistakes in Blazor really painful with the whole Edit, Compile, Debug cycle – something which Net 6.0 is meant to go some way to solve.

    Having just installed the preview components, NET 6.0 RC1 and Visual Studio 2022 Preview 4, Hot Reload is still completely broken for our most simple use-case above. If I modify a parameter in a Blazor component such as changing the word “Summary” in the string parameter named “Title” below and press the “Hot Reload” button in Visual Studio, then I get errors like “System.ArgumentException: ‘The renderer does not have a component with ID 39.'” followed by debugger then exiting.

    CashupSummary Title="Summary 1234"
    OnVarianceReasonEdit="HandleVarianceReasonEdit"
    Summary="CashupData.Summary">
    CashupSummary

    • Daniel RothMicrosoft employee

      Hi Phillip. There are a couple of known issues with hot reload and Razor that we’re working on getting a fix out for soon. However, I don’t recognize the specific error case you are describing. Do you see this error even with the default Blazor template? Is this a Blazor WebAssembly app or a Blazor Server app? Are you running with the debugger or not? It might be easiest for us to investigate this specific issue if you could create a Visual Studio feedback issue using the report a problem feature. Another thing you can try is using dotnet watch instead of performing the hot reload in Visual Studio and see if that works around the issue.

  • Mark Adamson

    The links with React and Angular sound great, and a nice surprise.

    We are predominantly using React on the front-end, but have some very useful and mature C# libraries for business logic, mostly small classes and static functions. I’m hoping that there might be a clever way to deploy these with Blazor Web Assembly and use them from React / javascript. It sounds like there wouldn’t be much of a leap from this new component interoperability, or maybe we can even do some clever hack with that.

  • Nick Whymark

    Hi Dan,

    Exciting stuff, thanks to you and the team for all your hard work! I wonder if you can point me to the details for Customizing Blazor WebAssembly packaging. Is there any documentation anywhere?

    Thanks!

  • Theunis van der Stoep

    Hi all,

    Just a small question from my side. Is RC1 ready and supported for production workloads? I read the .NET 6 RC1 blog from Richard, and he states that it is supported, just want to check if the same applies to ASP.NET Core?

    • Daniel RothMicrosoft employee

      We’re trying to get a fix out by end of week for the issue that breaks tag helpers and Blazor components when applying a hot reload. Other issues will get addressed in the next Visual Studio 2022 preview update. Please report any issues you hit though, as this will help us understand what the most important issues are to address.

  • Олег Нечитайло

    Regarding component generation:

    Does it work with Server-Side blazor or only with client-side?

    The reason is that server-side blazor is great to make highly dynamic components, something that you’d make using websockets normally.

    So while the most of the app is written in React/Angular, some components may require realtime updates, so it may be nice to write them on the server instead of making SignalR hubs yourself.