
Daniel
.NET Core 3.1 Preview 2 is now available. This release is primarily focused on bug fixes, but it contains a few new features as well.
Here’s what’s new in this release for ASP.NET Core:
- New component tag helper
- Prevent default actions for events in Blazor apps
- Stop event propagation in Blazor apps
- Validation of nested models in Blazor forms
- Detailed errors during Blazor app development
See the release notes for additional details and known issues.
Get started
To get started with ASP.NET Core in .NET Core 3.1 Preview 2 install the .NET Core 3.1 Preview 2 SDK.
If you’re on Windows using Visual Studio, for the best experience we recommend installing the latest preview of Visual Studio 2019 16.4. Installing Visual Studio 2019 16.4 will also install .NET Core 3.1 Preview 2, so you don’t need to separately install it. For Blazor development with .NET Core 3.1, Visual Studio 2019 16.4 is required.
Alongside this .NET Core 3.1 Preview 2 release, we’ve also released a Blazor WebAssembly update. To install the latest Blazor WebAssembly template also run the following command:
dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.1.0-preview2.19528.8
Upgrade an existing project
To upgrade an existing ASP.NET Core 3.1 Preview 1 project to 3.1 Preview 2:
- Update all Microsoft.AspNetCore.* package references to 3.1.0-preview2.19528.8
See also the full list of breaking changes in ASP.NET Core 3.1.
That’s it! You should now be all set to use .NET Core 3.1 Preview 2!
New component tag helper
Using Razor components from views and pages is now more convenient with the new component tag helper.
Previously, rendering a component from a view or page involved using the RenderComponentAsync
HTML helper.
@(await Html.RenderComponentAsync<Counter>(RenderMode.ServerPrerendered, new { IncrementAmount = 10 }))
The new component tag helper simplifies the syntax for rendering components from pages and views. Simply specify the type of the component you wish to render as well as the desired render mode. You can also specify component parameters using attributes prefixed with param-
.
<component type="typeof(Counter)" render-mode="ServerPrerendered" param-IncrementAmount="10" />
The different render modes allow you to control how the component is rendered:
RenderMode |
Description |
Static |
Renders the component into static HTML. |
Server |
Renders a marker for a Blazor Server application. This doesn’t include any output from the component. When the user-agent starts, it uses this marker to bootstrap the Blazor app. |
ServerPrerendered |
Renders the component into static HTML and includes a marker for a Blazor Server app. When the user-agent starts, it uses this marker to bootstrap the Blazor app. |
Prevent default actions for events in Blazor apps
You can now prevent the default action for events in Blazor apps using the new @oneventname:preventDefault
directive attribute. For example, the following component displays a count in a text box that can be changed by pressing the “+” or “-” keys:
<p>Press "+" or "-" in change the count.</p>
<input value="@count" @onkeypress="@KeyHandler" @onkeypress:preventDefault /> @code { int count = 0; void KeyHandler(KeyboardEventArgs ev) { if (ev.Key == "+") { count++; } else if (ev.Key == "-") { count--; } }
}
The @onkeypress:preventDefault
directive attribute prevents the default action of showing the text typed by the user in the text box. Specifying this attribute without a value is equivalent to @onkeypress:preventDefault="true"
. The value of the attribute can also be an expression: @onkeypress:preventDefault="shouldPreventDefault"
. You don’t have to define an event handler to prevent the default action; both features can be used independently.
Stop event propagation in Blazor apps
Use the new @oneventname:stopPropagation
directive attribute to stop event propagation in Blazor apps.
In the following example, checking the checkbox prevents click events from the child div
from propagating to the parent div
:
<input @bind="stopPropagation" type="checkbox" />
Parent div
Child div
</div> <button @onclick="OnClick">Click me!</button> @code { bool stopPropagation; void OnClickParentDiv() => Console.WriteLine("Parent div clicked."); void OnClickChildDiv() => Console.WriteLine("Child div clicked.");
}
Detailed errors during Blazor app development
When your Blazor app isn’t functioning properly during development, it’s important to get detailed error information so that you can troubleshoot and fix the issues. Blazor apps now display a gold bar at the bottom of the screen when an error occurs.
During development, in Blazor Server apps, the gold bar will direct you to the browser console where you can see the exception that has occurred.

In production, the gold bar notifies the user that something has gone wrong, and recommends the user to refresh the browser.

The UI for this error handling experience is part of the updated Blazor project templates so that it can be easily customized:
_Host.cshtml
An error has occurred. This application may no longer respond until reloaded. An unhandled exception has occurred. See browser dev tools for details.
Reload 🗙
Validation of nested models in Blazor forms
Blazor provides support for validating form input using data annotations with the built-in DataAnnotationsValidator
. However, the DataAnnotationsValidator
only validates top-level properties of the model bound to the form.
To validate the entire object graph of the bound model, try out the new ObjectGraphDataAnnotationsValidator
available in the experimental Microsoft.AspNetCore.Blazor.DataAnnotations.Validation package:
<EditForm Model="@model" OnValidSubmit="@HandleValidSubmit"> <ObjectGraphDataAnnotationsValidator /> ...
</EditForm>
The Microsoft.AspNetCore.Blazor.DataAnnotations.Validation is not slated to ship with .NET Core 3.1, but is provided as an experimental package to get early feedback.
Give feedback
We hope you enjoy the new features in this preview release of ASP.NET Core! Please let us know what you think by filing issues on GitHub.
Thanks for trying out ASP.NET Core!
Principal Program Manager, ASP.NET
Follow Daniel

Barry
SameSite is a 2016 extension to HTTP cookies intended to mitigate cross site request forgery (CSRF). The original design was an opt-in feature which could be used by adding a new SameSite property to cookies. It had two values, Lax and Strict. Setting the value to Lax indicated the cookie should be sent on navigation within the same site, or through GET navigation to your site from other sites. A value of Strict limited the cookie to requests which only originated from the same site. Not setting the property at all placed no restrictions on how the cookie flowed in requests. OpenIdConnect authentication operations (e.g. login, logout), and other features that send POST requests from an external site to the site requesting the operation, can use cookies for correlation and/or CSRF protection. These operations would need to opt-out of SameSite, by not setting the property at all, to ensure these cookies will be sent during their specialized request flows.
Google is now updating the standard and implementing their proposed changes in an upcoming version of Chrome. The change adds a new SameSite value, “None”, and changes the default behavior to “Lax”. This breaks OpenIdConnect logins, and potentially other features your web site may rely on, these features will have to use cookies whose SameSite property is set to a value of “None”. However browsers which adhere to the original standard and are unaware of the new value have a different behavior to browsers which use the new standard as the SameSite standard states that if a browser sees a value for SameSite it does not understand it should treat that value as “Strict”. This means your .NET website will now have to add user agent sniffing to decide whether you send the new None value, or not send the attribute at all.
.NET will issue updates to change the behavior of its SameSite attribute behavior in .NET 4.7.2 and in .NET Core 2.1 and above to reflect Google’s introduction of a new value. The updates for the .NET Framework will be available on November 19th as an optional update via Microsoft Update and WSUS if you use the “Check for Update” functionality. On December 10th it will become widely available and appear in Microsoft Update without you having to specifically check for updates. .NET Core updates will be available with .NET Core 3.1 starting with preview 1, in November.
.NET Core 3.1 will contain an updated enum definition, SameSite.Unspecified which will not set the SameSite property.
The OpenIdConnect middleware for Microsoft.Owin v4.1 and .NET Core will be updated at the same time as their .NET Framework and .NET updates, however we cannot introduce the user agent sniffing code into the framework, this must be implemented in your site code. The implementation of agent sniffing will vary according to what version of ASP.NET or ASP.NET Core you are using and the browsers you wish to support.
For ASP.NET 4.7.2 with Microsoft.Owin 4.1.0 agent sniffing can be implemented using ICookieManager;
public class SameSiteCookieManager : ICookieManager
{ private readonly ICookieManager _innerManager; public SameSiteCookieManager() : this(new CookieManager()) { } public SameSiteCookieManager(ICookieManager innerManager) { _innerManager = innerManager; } public void AppendResponseCookie(IOwinContext context, string key, string value, CookieOptions options) { CheckSameSite(context, options); _innerManager.AppendResponseCookie(context, key, value, options); } public void DeleteCookie(IOwinContext context, string key, CookieOptions options) { CheckSameSite(context, options); _innerManager.DeleteCookie(context, key, options); } public string GetRequestCookie(IOwinContext context, string key) { return _innerManager.GetRequestCookie(context, key); } private void CheckSameSite(IOwinContext context, CookieOptions options) { if (DisallowsSameSiteNone(context) && options.SameSite == SameSiteMode.None) { options.SameSite = null; } } public static bool DisallowsSameSiteNone(IOwinContext context) { // TODO: Use your User Agent library of choice here. var userAgent = context.Request.Headers["User-Agent"]; return userAgent.Contains("BrokenUserAgent") || userAgent.Contains("BrokenUserAgent2") }
}
And then configure OpenIdConnect settings to use the new CookieManager;
app.UseOpenIdConnectAuthentication( new OpenIdConnectAuthenticationOptions { // … Your preexisting options … CookieManager = new SameSiteCookieManager(new SystemWebCookieManager())
});
SystemWebCookieManager will need the .NET 4.7.2 or later SameSite patch installed to work correctly.
For ASP.NET Core you should install the patches and then implement the agent sniffing code within a cookie policy. For versions prior to 3.1 replace SameSiteMode.Unspecified with (SameSiteMode)(-1).
private void CheckSameSite(HttpContext httpContext, CookieOptions options)
{ if (options.SameSite > SameSiteMode.Unspecified) { var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); // TODO: Use your User Agent library of choice here. if (/* UserAgent doesn’t support new behavior */) { // For .NET Core < 3.1 set SameSite = -1 options.SameSite = SameSiteMode.Unspecified; } }
} public void ConfigureServices(IServiceCollection services)
{ services.Configure<CookiePolicyOptions>(options => { options.MinimumSameSitePolicy = SameSiteMode.Unspecified; options.OnAppendCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); options.OnDeleteCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); });
} public void Configure(IApplicationBuilder app)
{ app.UseCookiePolicy(); // Before UseAuthentication or anything else that writes cookies. app.UseAuthentication(); // …
}
Under testing with the Azure Active Directory team we have found the following checks work for all the common user agents that Azure Active Directory sees that don’t understand the new value.
public static bool DisallowsSameSiteNone(string userAgent)
{
// Cover all iOS based browsers here. This includes:
// - Safari on iOS 12 for iPhone, iPod Touch, iPad
// - WkWebview on iOS 12 for iPhone, iPod Touch, iPad
// - Chrome on iOS 12 for iPhone, iPod Touch, iPad
// All of which are broken by SameSite=None, because they use the iOS networking stack
if (userAgent.Contains("CPU iPhone OS 12") || userAgent.Contains("iPad; CPU OS 12"))
{
return true;
} // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes:
// - Safari on Mac OS X.
// This does not include:
// - Chrome on Mac OS X
// Because they do not use the Mac OS networking stack.
if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") && userAgent.Contains("Version/") && userAgent.Contains("Safari"))
{
return true;
} // Cover Chrome 50-69, because some versions are broken by SameSite=None, // and none in this range require it.
// Note: this covers some pre-Chromium Edge versions, // but pre-Chromium Edge does not require SameSite=None.
if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6"))
{
return true;
} return false;
}
This browser list is by no means canonical and you should validate that the common browsers and other user agents your system supports behave as expected once the update is in place.
Chrome 80 is scheduled to turn on the new behavior in February or March 2020, including a temporary mitigation added in Chrome 79 Beta. If you want to test the new behavior without the mitigation use Chromium 76. Older versions of Chromium are available for download.
If you cannot update your framework versions by the time Chrome turns the new behavior in early 2020 you may be able to change your OpenIdConnect flow to a Code flow, rather than the default implicit flow that ASP.NET and ASP.NET Core uses, but this should be viewed as a temporary measure.
We strongly encourage you to download the updated .NET Framework and .NET Core versions when they become available in November and start planning your update before Chrome’s changes are rolled out.

Daniel
.NET Core 3.1 Preview 1 is now available. This release is primarily focused on bug fixes, but it contains a few new features as well.
Here’s what’s new in this release for ASP.NET Core:
- Partial class support for Razor components
- Pass parameters to top-level components
- Support for shared queues in HttpSysServer
- Breaking changes for SameSite cookies
Alongside this .NET Core 3.1 Preview 1 release, we’ve also released a Blazor WebAssembly update, which now requires .NET Core 3.1. To use Blazor WebAssembly you will need to install .NET Core 3.1 Preview 1 as well as the latest preview of Visual Studio.
See the release notes for additional details and known issues.
Get started
To get started with ASP.NET Core in .NET Core 3.1 Preview 1 install the .NET Core 3.1 Preview 1 SDK.
If you’re on Windows using Visual Studio, for the best experience we recommend installing the latest preview of Visual Studio 2019 16.4. Installing Visual Studio 2019 16.4 will also install .NET Core 3.1 Preview 1, so you don’t need to separately install it. For Blazor development with .NET Core 3.1, Visual Studio 2019 16.4 is required.
To install the latest Blazor WebAssembly template run the following command:
dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.1.0-preview1.19508.20
Upgrade an existing project
To upgrade an existing ASP.NET Core 3.0 project to 3.1 Preview 1:
- Update any projects targeting netcoreapp3.0 to target netcoreapp3.1
- Update all Microsoft.AspNetCore.* package references to 3.1.0-preview1.19506.1
See also the full list of breaking changes in ASP.NET Core 3.1.
That’s it! You should now be all set to use .NET Core 3.1 Preview 1!
Partial class support for Razor components
Razor components are now generated as partial classes. You can author the code for a Razor component using a code-behind file defined as a partial class instead of defining all the code for the component in a single file.
For example, instead of defining the default Counter
component with an @code
block, like this:
Counter.razor
@page "/counter" <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button> @code { int currentCount = 0; void IncrementCount() { currentCount++; }
}
You can now separate out the code into a code-behind file using a partial class like this:
Counter.razor
@page "/counter" <h1>Counter</h1> <p>Current count: @currentCount</p> <button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
Counter.razor.cs
namespace BlazorApp1.Pages
{ public partial class Counter { int currentCount = 0; void IncrementCount() { currentCount++; } }
}
Pass parameters to top-level components
Blazor Server apps can now pass parameters to top-level components during the initial render. Previously you could only pass parameters to a top-level component with RenderMode.Static
. With this release, both RenderMode.Server
and RenderModel.ServerPrerendered
are now supported. Any specified parameter values are serialized as JSON and included in the initial response.
For example, you could prerender a Counter
component with a specific current count like this:
@(await Html.RenderComponentAsync<Counter>(RenderMode.ServerPrerendered, new { CurrentCount = 123 }))
Support for shared queues in HttpSysServer
In addition to the existing behavior where HttpSysServer created anonymous request queues, we’ve added to ability to create or attach to an existing named HTTP.sys request queue.
This should enable scenarios where the HTTP.Sys controller process that owns the queue is independent to the listener process making it possible to preserve existing connections and enqueued requests between across listener process restarts.
public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .ConfigureWebHostDefaults(webBuilder => { // ... webBuilder.UseHttpSys(options => { options.RequestQueueName = "MyExistingQueue", options.RequestQueueMode = RequestQueueMode.CreateOrAttach }) });
Breaking changes for SameSite cookies
This release updates the behavior of SameSite cookies in ASP.NET Core to conform to the latest standards being enforced by browsers. For details on these changes and their impact on existing apps see https://github.com/aspnet/Announcements/issues/390.
Give feedback
We hope you enjoy the new features in this preview release of ASP.NET Core! Please let us know what you think by filing issues on GitHub.
Thanks for trying out ASP.NET Core!
Principal Program Manager, ASP.NET
Follow Daniel

Daniel
Since the release of Blazor Server with .NET Core 3.0 last month lots of folks have shared their excitement with us about being able to build client-side web UI with just .NET and C#. At the same time, we’ve also heard lots of questions about what Blazor Server is, how it relates to Blazor WebAssembly, and what scenarios Blazor Server is best suited for. Should you choose Blazor Server for your client-side web UI needs, or wait for Blazor WebAssembly? This post seeks to answer these questions, and to provide insights into how Blazor Server performs at scale and how we envision Blazor evolving in the future.
What is Blazor Server?
Blazor Server apps host Blazor components on the server and handle UI interactions over a real-time SignalR connection. As the user interacts with the app, the UI events are sent to the server over the connection to be handled by the various components that make up the app. When a component handles a UI event, it’s rendered based on its updated state. Blazor compares the newly rendered output with what was rendered previously and send the changes back to the browser and applies them to the DOM.

Since Blazor Server apps run on .NET Core on the server, they enjoy all the benefits of running on .NET Core including great runtime performance and tooling. Blazor Server apps can leverage the full ecosystem of .NET Standard libraries without any browser imposed limitations.
When should I use Blazor Server?
Blazor Server enables you to add rich interactive UI to your .NET apps today without having to write JavaScript. If you need the interactivity of a single-page app in your .NET app, then Blazor Server is a great solution.
Blazor Server can be used to write completely new apps or to complement existing MVC and Razor Pages apps. There’s no need to rewrite existing app logic. Blazor is designed to work together with MVC and Razor Pages, not replace them. You can continue to use MVC and Razor Pages for your server-rendering needs while using Blazor for client-side UI interactions.
Blazor Server works best for scenarios where you have a reliable low-latency network connection, which is normally achieved when the client and server are geographically on the same continent. Apps that require extremely high fidelity instant updates on every tiny mouse twitch, like real-time games or drawing apps, are not a good fit for Blazor Server. Because Blazor Server apps require an active network connection, offline scenarios are not supported.
Blazor Server is also useful when you want to offload work from the client to the server. Blazor Server apps require only a small download to establish the connection with the server and to process UI interactions. All the hard work of running the app logic and rendering the UI is then done on the server. This means Blazor Server apps load fast even as the app functionality grows. Because the client side of a Blazor Server app is so thin, it’s a great solution for apps that need to run on low-powered devices.
Using Blazor Server at scale
Blazor Server can scale from small internal line of business apps to large internet scale apps. While .NET Core 3.0 was still in preview we tested Blazor Server to see what its baseline scale characteristics look like. We put a Blazor Server app under load with active clients and monitored the latency of the user interactions. In our tests, a single Standard_D1_v2 instance on Azure (1 vCPU, 3.5 GB memory) could handle over 5,000 concurrent users without any degradation in latency. A Standard_D3_V2 instance (4 vCPU, 14GB memory) handled well over 20,000 concurrent clients. The main bottleneck for handling further load was available memory. Will you see this level of scale in your own app? That will depend in large part on how much additional memory your app requires per user. But for many apps, we believe this level of scale out is quite reasonable. We also plan to post additional updates on improvements in Blazor Server scalability in the weeks ahead. So stay tuned!
What is Blazor WebAssembly?
Blazor is a UI framework that can run in different environments. When you build UI components using Blazor, you get the flexibility to choose how and where they are hosted and run. As well as running your UI components on the server with Blazor Server, you can run those same components on the client with Blazor WebAssembly. This flexibility means you can adapt to your users’ needs and avoid the risk of being tied to a specific app hosting model.
Blazor WebAssembly apps host components in the browser using a WebAssembly-based .NET runtime. The components handle UI events and execute their rendering logic directly in the browser. Blazor WebAssembly apps use only open web standards to run .NET code client-side, without the need for any browser plugins or code transpilation. Just like with Blazor Server apps, the Blazor framework handles comparing the newly rendered output with what was rendered previous and updates the DOM accordingly, but with Blazor WebAssembly the UI rendering is handled client-side.

When should I use Blazor WebAssembly?
Blazor WebAssembly is still in preview and isn’t yet ready for production use yet. If you’re looking for a production ready solution, then Blazor Server is what we’d recommend.
Once Blazor WebAssembly ships (May 2020), it will enable running Razor components and .NET code in the browser on the user’s device. Blazor WebAssembly apps help offload work from the server to the client. A Blazor WebAssembly app can leverage the client device’s compute, memory, and storage resources, as well as other resources made available through standard browser APIs.
Blazor WebAssembly apps don’t require the use of .NET on the server and can be used to build static sites. A Blazor WebAssembly app is just a bunch of static files that can be hosted using any static site hosting solution, like GitHub pages or Azure Static Website Hosting. When combined with a service worker, a Blazor WebAssembly app can function completely offline.
When combined with .NET on the server, Blazor WebAssembly enables full stack web development. You can share code, leverage the .NET ecosystem, and reuse your existing .NET skills and infrastructure.
Including a .NET runtime with your web app does increase the app size, which will impact load time. While there are a variety of techniques to mitigate this (prerendering on the server, HTTP caching, IL linking, etc.), Blazor WebAssembly may not be the best choice for apps that are very sensitive to download size and load time.
Blazor WebAssembly apps also require a browser that supports WebAssembly. WebAssembly is supported by all modern browsers, including mobile and desktop browsers. However, if you need to support older browsers without WebAssembly support then Blazor WebAssembly isn’t for you.
Blazor WebAssembly is optimized for UI rendering scenarios, but isn’t currently great for running CPU intensive workloads. Blazor WebAssembly apps today use a .NET IL interpreter to execute your .NET code, which doesn’t have the same performance as a native .NET runtime with JIT compilation. We’re working to better address this scenario in the future by adding support for compiling your .NET code directly to WebAssembly instead of using an interpreter.
You can change your mind later
Regardless of whether you choose Blazor Server or Blazor WebAssembly, you can always change your mind later. All Blazor apps use a common component model, Razor components. The same components can be hosted in a Blazor Server app or a Blazor WebAssembly app. So if you start with one Blazor hosting model and then later decide you want to switch to a different one, doing so is very straight forward.
What’s next for Blazor?
After shipping Blazor WebAssembly, we plan to expand Blazor to support not just web apps, but also Progressive Web Apps (PWAs), hybrid apps, and even fully native apps.
- Blazor PWAs: PWAs are web apps that leverage the latest web standards to provide a more native-like experience. PWAs can support offline scenarios, push notifications, and OS integrations, like support for pinning the app to your home screen or the Windows Start menu.
- Blazor Hybrid: Hybrid apps are native apps that use web technologies for the UI. Examples include Electron apps and mobile apps that render to a web view. Blazor Hybrid apps don’t run on WebAssembly, but instead use a native .NET runtime like .NET Core or Xamarin. You can find an experimental sample for using Blazor with Electron on GitHub.
- Blazor Native: Blazor apps today render HTML, but the renderer can be replaced to render native controls instead. A Blazor Native app runs natively on the devices and uses a common UI abstraction to render native controls for that device. This is very similar to how frameworks like Xamarin Forms or React Native work today.
These three efforts are all currently experimental. We expect to have official previews of support for Blazor PWAs and Blazor Hybrid apps using Electron in the .NET 5 time frame (Nov 2020). There isn’t a road map for Blazor Native support yet, but it’s an area we are actively investigating.
Summary
With .NET Core 3.0, you can build rich interactive client-side UI today with Blazor Server. Blazor Server is a great way to add client-side functionality to your existing and new web apps using your existing .NET skills and assets. Blazor Server is built to scale for all your web app needs. Blazor WebAssembly is still in preview, but is expected to ship in May of next year. In the future we expect to continue to evolve Blazor to support PWAs, hybrid apps, and native apps. For now, we hope you’ll give Blazor Server a try by installing .NET Core 3.0!
Principal Program Manager, ASP.NET
Follow Daniel

Daniel
Today we are thrilled to announce the release of .NET Core 3.0! .NET Core 3.0 is ready for production use, and is loaded with lots of great new features for building amazing web apps with ASP.NET Core and Blazor.
Some of the big new features in this release of ASP.NET Core include:
- Build rich interactive client-side web apps using C# instead of JavaScript using Blazor).
- Create high-performance backend services with gRPC.
- SignalR now has support for automatic reconnection and client-to-server streaming.
- Generate strongly typed client code for Web APIs with OpenAPI documents.
- Endpoint routing integrated through the framework.
- HTTP/2 now enabled by default in Kestrel.
- Authentication support for Web APIs and single-page apps integrated with IdentityServer
- Support for certificate and Kerberos authentication.
- Integrates with the new System.Text.Json serializer.
- New generic host sets up common hosting services like dependency injection (DI), configuration, and logging.
- New Worker Service template for building long-running services.
- New EventCounters created for requests per second, total requests, current requests, and failed requests.
- Startup errors now reported to the Windows Event Log when hosted in IIS.
- Request pipeline integrated with with System.IO.Pipelines.
- Performance improvements across the entire stack.
You can find all the details about what’s new in ASP.NET Core in .NET Core 3.0 in the What’s new in ASP.NET Core 3.0 topic.
See the .NET Core 3.0 release notes for additional details and known issues.
Get started
To get started with ASP.NET Core in .NET Core 3.0 install the .NET Core 3.0 SDK.
If you’re on Windows using Visual Studio, install Visual Studio 2019 16.3, which includes .NET Core 3.0.
Note: .NET Core 3.0 requires Visual Studio 2019 16.3 or later.
There is also a Blazor WebAssembly preview update available with this release. This update to Blazor WebAssembly still has a Preview 9 version, but carries an updated build number. Blazor WebAssembly is still in preview and is not part of the .NET Core 3.0 release.
To install the latest Blazor WebAssembly template run the following command:
dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview9.19465.2
Upgrade an existing project
To upgrade an existing ASP.NET Core app to .NET Core 3.0, follow the migrations steps in the ASP.NET Core docs.
See the full list of breaking changes in ASP.NET Core 3.0.
To upgrade an existing ASP.NET Core 3.0 RC1 project to 3.0:
- Update all Microsoft.AspNetCore.* and Microsoft.Extensions.* package references to 3.0.0
- Update all Microsoft.AspNetCore.Blazor.* package references to 3.0.0-preview9.19465.2
That’s it! You should now be all set to use .NET Core 3.0!
Join us at .NET Conf!
Please join us at .NET Conf to learn all about the new features in .NET Core 3.0 and to celebrate the release with us! .NET Conf is a live streaming event open to everyone, and features talks from many talented speakers from the .NET team and the .NET community. Check out the schedule and attend a local event near you. Or join the Virtual Attendee Party for the chance to win prizes!
Give feedback
We hope you enjoy the new features in this release of ASP.NET Core and Blazor in .NET Core 3.0! We are eager to hear about your experiences with this latest .NET Core release. Let us know what you think by filing issues on GitHub.
Thanks for using ASP.NET Core and Blazor!
Principal Program Manager, ASP.NET
Follow Daniel

Daniel
.NET Core 3.0 Release Candidate 1 (RC1) is now available. This release contains only a handful of bug fixes and closely represents what we expect to release for .NET Core 3.0.
Please see the release notes for additional details and known issues.
Get started
To get started with ASP.NET Core in .NET Core 3.0 RC1 install the .NET Core 3.0 RC1 SDK.
If you’re on Windows using Visual Studio, install the latest preview of Visual Studio 2019.
.NET Core 3.0 RC1 requires Visual Studio 2019 16.3 Preview 4 or later.
There is also a Blazor WebAssembly preview update available with this release. This update to Blazor WebAssembly still has a Preview 9 version, but carries an updated build number. This is not a release candidate for Blazor WebAssembly. Blazor WebAssembly isn’t expected to ship as a stable release until some time after .NET Core 3.0 ships (details coming soon!).
To install the latest Blazor WebAssembly template run the following command:
dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview9.19457.4
Upgrade an existing project
To upgrade an existing ASP.NET Core app to .NET Core 3.0 Preview 9, follow the migrations steps in the ASP.NET Core docs.
Please also see the full list of breaking changes in ASP.NET Core 3.0.
To upgrade an existing ASP.NET Core 3.0 Preview 9 project to RC1:
- Update all Microsoft.AspNetCore.* package references to 3.0.0-rc1.19457.4
- Update all Microsoft.AspNetCore.Blazor.* package references to 3.0.0-preview9.19457.4
That’s it You should now be all set to use .NET Core 3.0 RC1!
Give feedback
We hope you enjoy the new features in this preview release of ASP.NET Core and Blazor! Please let us know what you think by filing issues on GitHub.
Thanks for trying out ASP.NET Core and Blazor!
Principal Program Manager, ASP.NET
Follow Daniel

Daniel
.NET Core 3.0 Preview 9 is now available and it contains a number of improvements and updates to ASP.NET Core and Blazor.
Here’s the list of what’s new in this preview:
- Blazor event handlers and data binding attributes moved to Microsoft.AspNetCore.Components.Web
- Blazor routing improvements
- Render content using a specific layout
- Routing decoupled from authorization
- Route to components from multiple assemblies
- Render multiple Blazor components from MVC views or pages
- Smarter reconnection for Blazor Server apps
- Utility base component classes for managing a dependency injection scope
- Razor component unit test framework prototype
- Helper methods for returning Problem Details from controllers
- New client API for gRPC
- Support for async streams in streaming gRPC responses
Please see the release notes for additional details and known issues.
Get started
To get started with ASP.NET Core in .NET Core 3.0 Preview 9 install the .NET Core 3.0 Preview 9 SDK.
If you’re on Windows using Visual Studio, install the latest preview of Visual Studio 2019.
.NET Core 3.0 Preview 9 requires Visual Studio 2019 16.3 Preview 3 or later.
To install the latest Blazor WebAssembly template also run the following command:
dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.0.0-preview9.19424.4
Upgrade an existing project
To upgrade an existing ASP.NET Core app to .NET Core 3.0 Preview 9, follow the migrations steps in the ASP.NET Core docs.
Please also see the full list of breaking changes in ASP.NET Core 3.0.
To upgrade an existing ASP.NET Core 3.0 Preview 8 project to Preview 9:
- Update all Microsoft.AspNetCore.* package references to 3.0.0-preview9.19424.4
- In Blazor apps and libraries:
- Add a using statement for
Microsoft.AspNetCore.Components.Web
in your top level _Imports.razor file (see Blazor event handlers and data binding attributes moved to Microsoft.AspNetCore.Components.Web below for details)
- Add a using statement for
Microsoft.AspNetCore.Components.Authorization
in your top level _Imports.razor file.
- Update all Blazor component parameters to be public.
- Update implementations of
IJSRuntime
to return ValueTask<T>
.
- Replace calls to
MapBlazorHub<TComponent>
with a single call to MapBlazorHub
.
- Update calls to
RenderComponentAsync
and RenderStaticComponentAsync
to use the new overloads to RenderComponentAsync
that take a RenderMode
parameter (see Render multiple Blazor components from MVC views or pages below for details).
- Update App.razor to use the updated
Router
component (see Blazor routing improvements below for details).
- (Optional) Remove page specific _Imports.razor file with the
@layout
directive to use the default layout specified through the router instead.
- Remove any use of the
PageDisplay
component and replace with LayoutView
, RouteView
, or AuthorizeRouteView
as appropriate (see Blazor routing improvements below for details).
- Replace uses of
IUriHelper
with NavigationManager
.
- Remove any use of
@ref:suppressField
.
- Replace the previous
RevalidatingAuthenticationStateProvider
code with the new RevalidatingIdentityAuthenticationStateProvider
code from the project template.
- Replace
Microsoft.AspNetCore.Components.UIEventArgs
with System.EventArgs
and remove the “UI” prefix from all EventArgs
derived types (UIChangeEventArgs
-> ChangeEventArgs
, etc.).
- Replace
DotNetObjectRef
with DotNetObjectReference
.
- Replace
OnAfterRender()
and OnAfterRenderAsync()
implementations with OnAfterRender(bool firstRender)
or OnAfterRenderAsync(bool firstRender)
.
- In gRPC projects:
- Update calls to
GrpcClient.Create
with a call GrpcChannel.ForAddress
to create a new gRPC channel and new up your typed gRPC clients using this channel.
- Rebuild any project or project dependency that uses gRPC code generation for an ABI change in which all clients inherit from
ClientBase
instead of LiteClientBase
. There are no code changes required for this change.
- Please also see the grpc-dotnet announcement for all changes.
You should now be all set to use .NET Core 3.0 Preview 9!
Blazor event handlers and data binding attributes moved to Microsoft.AspNetCore.Components.Web
In this release we moved the set of bindings and event handlers available for HTML elements into the Microsoft.AspNetCore.Components.Web.dll assembly and into the Microsoft.AspNetCore.Components.Web
namespace. This change was made to isolate the web specific aspects of the Blazor programming from the core programming model. This section provides additional details on how to upgrade your existing projects to react to this change.
Blazor apps
Open the application’s root _Imports.razor and add @using Microsoft.AspNetCore.Components.Web
. Blazor apps get a reference to the Microsoft.AspNetCore.Components.Web package implicitly without any additional package references, so adding a reference to this package isn’t necessary.
Blazor libraries
Add a package reference to the Microsoft.AspNetCore.Components.Web package package if you don’t already have one. Then open the root _Imports.razor file for the project (create the file if you don’t already have it) and add @using Microsoft.AspNetCore.Components.Web
.
Troubleshooting guidance
With the correct references and using statement for Microsoft.AspNetCore.Components.Web
, event handlers like @onclick
and @bind
should be bold font and colorized as shown below when using Visual Studio.

If @bind
or @onclick
are colorized as a normal HTML attribute, then the @using
statement is missing.

If you’re missing a using statement for the Microsoft.AspNetCore.Components.Web
namespace, you may see build failures. For example, the following build error for the code shown above indicates that the @bind
attribute wasn’t recognized:
CS0169 The field 'Index.text' is never used
CS0428 Cannot convert method group 'Submit' to non-delegate type 'object'. Did you intend to invoke the method?
In other cases you may get a runtime exception and the app fails to render. For example, the following runtime exception seen in the browser console indicates that the @onclick
attribute wasn’t recognized:
Error: There was an error applying batch 2.
DOMException: Failed to execute 'setAttribute' on 'Element': '@onclick' is not a valid attribute name.
Add a using statement for the Microsoft.AspNetCore.Components.Web
namespace to address these issues. If adding the using statement fixed the problem, consider moving to the using statement app’s root _Imports.razor so it will apply to all files.
If you add the Microsoft.AspNetCore.Components.Web
namespace but get the following build error, then you’re missing a package reference to the Microsoft.AspNetCore.Components.Web package:
CS0234 The type or namespace name 'Web' does not exist in the namespace 'Microsoft.AspNetCore.Components' (are you missing an assembly reference?)
Add a package reference to the Microsoft.AspNetCore.Components.Web package to address the issue.
Blazor routing improvements
In this release we’ve revised the Blazor Router
component to make it more flexible and to enable new scenarios. The Router
component in Blazor handles rendering the correct component that matches the current address. Routable components are marked with the @page
directive, which adds the RouteAttribute
to the generated component classes. If the current address matches a route, then the Router
renders the contents of its Found
parameter. If no route matches, then the Router
component renders the contents of its NotFound
parameter.
To render the component with the matched route, use the new RouteView
component passing in the supplied RouteData
from the Router
along with any desired parameters. The RouteView
component will render the matched component with its layout if it has one. You can also optionally specify a default layout to use if the matched component doesn’t have one.
<Router AppAssembly="typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" /> </Found> <NotFound> <h1>Page not found</h1> <p>Sorry, but there's nothing here!</p> </NotFound>
</Router>
Render content using a specific layout
To render a component using a particular layout, use the new LayoutView
component. This is useful when specifying content for not found pages that you still want to use the app’s layout.
<Router AppAssembly="typeof(Program).Assembly"> <Found Context="routeData"> <RouteView RouteData="routeData" DefaultLayout="typeof(MainLayout)" /> </Found> <NotFound> <LayoutView Layout="typeof(MainLayout)"> <h1>Page not found</h1> <p>Sorry, but there's nothing here!</p> </LayoutView> </NotFound>
</Router>
Routing decoupled from authorization
Authorization is no longer handled directly by the Router
. Instead, you use the AuthorizeRouteView
component. The AuthorizeRouteView
component is a RouteView
that will only render the matched component if the user is authorized. Authorization rules for specific components are specified using the AuthorizeAttribute
. The AuthorizeRouteView
component also sets up the AuthenticationState
as a cascading value if there isn’t one already. Otherwise, you can still manually setup the AuthenticationState
as a cascading value using the CascadingAuthenticationState
component.
<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" /> </Found> <NotFound> <CascadingAuthenticationState> <LayoutView Layout="@typeof(MainLayout)"> <p>Sorry, there's nothing at this address.</p> </LayoutView> </CascadingAuthenticationState> </NotFound>
</Router>
You can optionally set the NotAuthorized
and Authorizing
parameters of the AuthorizedRouteView
component to specify content to display if the user is not authorized or authorization is still in progress.
<Router AppAssembly="@typeof(Program).Assembly"> <Found Context="routeData"> <AuthorizeRouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)"> <NotAuthorized> <p>Nope, nope!</p> </NotAuthorized> </AuthorizeRouteView> </Found>
</Router>
Route to components from multiple assemblies
You can now specify additional assemblies for the Router
component to consider when searching for routable components. These assemblies will be considered in addition to the specified AppAssembly
. You specify these assemblies using the AdditionalAssemblies
parameter. For example, if Component1
is a routable component defined in a referenced class library, then you can support routing to this component like this:
<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="new[] { typeof(Component1).Assembly }> ...
</Router>
Render multiple Blazor components from MVC views or pages
We’ve reenabled support for rendering multiple components from a view or page in a Blazor Server app. To render a component from a .cshtml file, use the Html.RenderComponentAsync<TComponent>(RenderMode renderMode, object parameters)
HTML helper method with the desired RenderMode
.
RenderMode |
Description |
Supports parameters? |
Static |
Statically render the component with the specified parameters. |
Yes |
Server |
Render a marker where the component should be rendered interactively by the Blazor Server app. |
No |
ServerPrerendered |
Statically prerender the component along with a marker to indicate the component should later be rendered interactively by the Blazor Server app. |
No |
Support for stateful prerendering has been removed in this release due to security concerns. You can no longer prerender components and then connect back to the same component state when the app loads. We may reenable this feature in a future release post .NET Core 3.0.
Blazor Server apps also no longer require that the entry point components be registered in the app’s Configure
method. Only a single call to MapBlazorHub()
is required.
Smarter reconnection for Blazor Server apps
Blazor Server apps are stateful and require an active connection to the server in order to function. If the network connection is lost, the app will try to reconnect to the server. If the connection can be reestablished but the server state is lost, then reconnection will fail. Blazor Server apps will now detect this condition and recommend the user to refresh the browser instead of retrying to connect.

Utility base component classes for managing a dependency injection scope
In ASP.NET Core apps, scoped services are typically scoped to the current request. After the request completes, any scoped or transient services are disposed by the dependency injection (DI) system. In Blazor Server apps, the request scope lasts for the duration of the client connection, which can result in transient and scoped services living much longer than expected.
To scope services to the lifetime of a component you can use the new OwningComponentBase
and OwningComponentBase<TService>
base classes. These base classes expose a ScopedServices
property of type IServiceProvider
that can be used to resolve services that are scoped to the lifetime of the component. To author a component that inherits from a base class in Razor use the @inherits
directive.
@page "/users"
@attribute [Authorize]
@inherits OwningComponentBase<Data.ApplicationDbContext> <h1>Users (@Service.Users.Count())</h1>
<ul> @foreach (var user in Service.Users) { <li>@user.UserName</li> }
</ul>
Note: Services injected into the component using @inject
or the InjectAttribute
are not created in the component’s scope and will still be tied to the request scope.
Razor component unit test framework prototype
We’ve started experimenting with building a unit test framework for Razor components. You can read about the prototype in Steve Sanderson’s Unit testing Blazor components – a prototype blog post. While this work won’t ship with .NET Core 3.0, we’d still love to get your feedback early in the design process. Take a look at the code on GitHub and let us know what you think!
Helper methods for returning Problem Details from controllers
Problem Details is a standardized format for returning error information from an HTTP endpoint. We’ve added new Problem
and ValidationProblem
method overloads to controllers that use optional parameters to simplify returning Problem Detail responses.
[Route("/error")]
public ActionResult<ProblemDetails> HandleError()
{ return Problem(title: "An error occurred while processing your request", statusCode: 500);
}
New client API for gRPC
To improve compatibility with the existing Grpc.Core
implementation, we’ve changed our client API to use gRPC channels. The channel is where gRPC configuration is set and it is used to create strongly typed clients. The new API provides a more consistent client experience with Grpc.Core
, making it easier to switch between using the two libraries.
// Old
using var httpClient = new HttpClient() { BaseAddress = new Uri("https://localhost:5001") };
var client = GrpcClient.Create<GreeterClient>(httpClient); // New
var channel = GrpcChannel.ForAddress("https://localhost:5001");
var client = new GreeterClient(channel); var reply = await client.GreetAsync(new HelloRequest { Name = "Santa" });
Support for async streams in streaming gRPC responses
gRPC streaming responses return a custom IAsyncStreamReader
type that can be iterated on to receive all response messages in a streaming response. With the addition of async streams in C# 8, we’ve added a new extension method that makes for a more ergonomic API while consuming streaming responses.
// Old
while (await requestStream.MoveNext(CancellationToken.None))
{ var message = requestStream.Current; // …
} // New and improved
await foreach (var message in requestStream.ReadAllAsync())
{ // …
}
Give feedback
We hope you enjoy the new features in this preview release of ASP.NET Core and Blazor! Please let us know what you think by filing issues on GitHub.
Thanks for trying out ASP.NET Core and Blazor!
Principal Program Manager, ASP.NET
Follow Daniel
Overview
Since its inception, the .NET Core configuration provider for Azure App Configuration has provided the capability to monitor changes and sync them to the configuration within a running application. We recently redesigned this functionality to allow for on-demand refresh of the configuration. The new design paves the way for smarter applications that only refresh the configuration when necessary. As a result, inactive applications no longer have to monitor for configuration changes unnecessarily.
Initial design : Timer-based watch
In the initial design, configuration was kept in sync with Azure App Configuration using a watch mechanism which ran on a timer. At the time of initialization of the Azure App Configuration provider, users could specify the configuration settings to be updated and an optional polling interval. In case the polling interval was not specified, a default value of 30 seconds was used.
public static IWebHost BuildWebHost(string[] args)
{ WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { // Load settings from Azure App Configuration // Set up the provider to listen for changes triggered by a sentinel value var settings = config.Build(); string appConfigurationEndpoint = settings["AzureAppConfigurationEndpoint"]; config.AddAzureAppConfiguration(options => { options.ConnectWithManagedIdentity(appConfigurationEndpoint) .Use(keyFilter: "WebDemo:*") .WatchAndReloadAll(key: "WebDemo:Sentinel", label: LabelFilter.Null); }); settings = config.Build(); }) .UseStartup<Startup>() .Build();
}
For example, in the above code snippet, Azure App Configuration would be pinged every 30 seconds for changes. These calls would be made irrespective of whether the application was active or not. As a result, there would be unnecessary usage of network and CPU resources within inactive applications. Applications needed a way to trigger a refresh of the configuration on demand in order to be able to limit the refreshes to active applications. Then unnecessary checks for changes could be avoided.
This timer-based watch mechanism had the following fundamental design flaws.
- It could not be invoked on-demand.
- It continued to run in the background even in applications that could be considered inactive.
- It promoted constant polling of configuration rather than a more intelligent approach of updating configuration when applications are active or need to ensure freshness.
New design : Activity-based refresh
The new refresh mechanism allows users to keep their configuration updated using a middleware to determine activity. As long as the ASP.NET Core web application continues to receive requests, the configuration settings continue to get updated with the configuration store.
The application can be configured to trigger refresh for each request by adding the Azure App Configuration middleware from package Microsoft.Azure.AppConfiguration.AspNetCore
in your application’s startup code.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{ app.UseAzureAppConfiguration(); app.UseMvc();
}
At the time of initialization of the configuration provider, the user can use the ConfigureRefresh
method to register the configuration settings to be updated with an optional cache expiration time. In case the cache expiration time is not specified, a default value of 30 seconds is used.
public static IWebHost BuildWebHost(string[] args)
{ WebHost.CreateDefaultBuilder(args) .ConfigureAppConfiguration((hostingContext, config) => { // Load settings from Azure App Configuration // Set up the provider to listen for changes triggered by a sentinel value var settings = config.Build(); string appConfigurationEndpoint = settings["AzureAppConfigurationEndpoint"]; config.AddAzureAppConfiguration(options => { options.ConnectWithManagedIdentity(appConfigurationEndpoint) .Use(keyFilter: "WebDemo:*") .ConfigureRefresh((refreshOptions) => { // Indicates that all settings should be refreshed when the given key has changed refreshOptions.Register(key: "WebDemo:Sentinel", label: LabelFilter.Null, refreshAll: true); }); }); settings = config.Build(); }) .UseStartup<Startup>() .Build();
}
In order to keep the settings updated and avoid unnecessary calls to the configuration store, an internal cache is used for each setting. Until the cached value of a setting has expired, the refresh operation does not update the value. This happens even when the value has changed in the configuration store.
Try it now!
For more information about Azure App Configuration, check out the following resources. You can find step-by-step tutorials that would help you get started with dynamic configuration using the new refresh mechanism within minutes. Please let us know what you think by filing issues on GitHub.
Overview: Azure App configuration
Tutorial: Use dynamic configuration in an ASP.NET Core app
Tutorial: Use dynamic configuration in a .NET Core app
Related Blog: Configuring a Server-side Blazor app with Azure App Configuration
Software Engineer, Azure App Configuration
Follow