ASP.NET Core updates in .NET 5 Preview 8

Daniel Roth

.NET 5 Preview 8 is now available and is ready for evaluation. Here’s what’s new in this release:

  • Azure Active Directory authentication with Microsoft.Identity.Web
  • CSS isolation for Blazor components
  • Lazy loading in Blazor WebAssembly
  • Updated Blazor WebAssembly globalization support
  • New InputRadio Blazor component
  • Set UI focus in Blazor apps
  • Influencing the HTML head in Blazor apps
  • Control Blazor component instantiation
  • Protected browser storage
  • Model binding and validation with C# 9 record types
  • Improvements to DynamicRouteValueTransformer
  • Auto refresh with dotnet watch
  • Console Logger Formatter
  • JSON Console Logger

See the .NET 5 release notes for additional details and known issues.

Get started

To get started with ASP.NET Core in .NET 5 Preview 8 install the .NET 5 SDK.

You need to use Visual Studio 2019 16.8 Preview 2 or newer to use .NET 5 Preview 8. .NET 5 is also supported with the latest preview of Visual Studio for Mac. To use .NET 5 with Visual Studio Code, install the latest version of the C# extension.

Upgrade an existing project

To upgrade an existing ASP.NET Core app from .NET 5 Preview 7 to .NET 5 Preview 8:

  • Update all Microsoft.AspNetCore.* package references to 5.0.0-preview.8.*.
  • Update all Microsoft.Extensions.* package references to 5.0.0-preview.8.*.
  • Update System.Net.Http.Json package references to 5.0.0-preview.8.*.

That’s it! You should be all ready to go.

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

What’s new?

Azure Active Directory authentication with Microsoft.Identity.Web

The ASP.NET Core project templates now integrate with Microsoft.Identity.Web to handle authentication with Azure Activity Directory (Azure AD). The Microsoft.Identity.Web package provides a better experience for authentication through Azure AD as well as an easier way to access Azure resources on behalf of your users, including Microsoft Graph. Check out the Microsoft.Identity.Web sample that take you from a simple login through multi-tenancy, using Azure APIs, using Microsoft Graph, and protecting your own APIs. Microsoft.Identity.Web will be generally available alongside .NET 5.

CSS isolation for Blazor components

Blazor now supports defining CSS styles that are scoped to a given component. Component specific CSS styles make it easier to reason about the styles in your app and to avoid unintentional side effects from global styles. You define component specific styles in a .razor.css file the matches the name of the .razor file for the component.

For example, let’s say you have a component MyComponent.razor file that looks like this:

MyComponent.razor

<h1>My Component</h1>

<ul class="cool-list">
    <li>Item1</li>
    <li>Item2</li>
</ul>

You can then define a MyComponent.razor.css with the styles for MyComponent:

MyComponent.razor.css

h1 {
    font-family: 'Comic Sans MS'
}

.cool-list li {
    color: red;
}

The styles in MyComponent.razor.css will only get applied to the rendered output of MyComponent; the h1 elements rendered by other components, for example, are not affected.

To write a selector in component specific styles that affects child components, use the ::deep combinator.

.parent ::deep .child {
    color: red;
}

By using the ::deep combinator, only the .parent class selector is scoped to the component; the .child class selector is not scoped, and will match content from child components.

Blazor achieves CSS isolation by rewriting the CSS selectors as part of the build so that they only match markup rendered by the component. Blazor then bundles together the rewritten CSS files and makes the bundle available to the app as a static web asset at the path _framework/scoped.styles.css.

While Blazor doesn’t natively support CSS preprocessors like Sass or Less, you can still use CSS preprocessors to generate component specific styles before they are rewritten as part of the building the project.

Lazy loading in Blazor WebAssembly

Lazy loading enables you to improve the load time of your Blazor WebAssembly app by deferring the download of specific app dependencies until they are required. Lazy loading may be helpful if your Blazor WebAssembly app has large dependencies that are only used for specific parts of the app.

To delay the loading of an assembly, you add it to the BlazorWebAssemblyLazyLoad item group in your project file:

Assemblies marked for lazy loading must be explicitly loaded by the app before they are used. To lazy load assemblies at runtime, use the LazyAssemblyLoader service:

@inject LazyAssemblyLoader LazyAssemblyLoader

@code {
    var assemblies = await LazyAssemblyLoader.LoadAssembliesAsync(new string[] { "Lib1.dll" });
}

To lazy load assemblies for a specific page, use the OnNavigateAsync event on the Router component. The OnNavigateAsync event is fired on every page navigation and can be used to lazy load assemblies for a particular route. You can also lazily load the entire page for a route by passing any lazy loaded assemblies as additional assemblies to the Router.

The following examples demonstrates using the LazyAssemblyLoader service to lazy load a specific dependency (Lib1.dll) when the user navigates to /page1. The lazy loaded assembly is then added to the additional assemblies list passed to the Router component, so that it can discover any routable components in that assembly.

@using System.Reflection
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.WebAssembly.Services
@inject LazyAssemblyLoader LazyAssemblyLoader

<Router AppAssembly="typeof(Program).Assembly" AdditionalAssemblies="lazyLoadedAssemblies" OnNavigateAsync="@OnNavigateAsync">
    <Navigating>
        <div>
            <p>Loading the requested page...</p>
        </div>
    </Navigating>
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="typeof(MainLayout)" />
    </Found>
    <NotFound>
        <LayoutView Layout="typeof(MainLayout)">
            <p>Sorry, there is nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

@code {
    private List<Assembly> lazyLoadedAssemblies = 
        new List<Assembly>();

    private async Task OnNavigateAsync(NavigationContext args)
    {
        if (args.Path.EndsWith("/page1"))
        {
            var assemblies = await LazyAssemblyLoader.LoadAssembliesAsync(new string[] { "Lib1.dll"  });
            lazyLoadedAssemblies.AddRange(assemblies);
        }
    }
}

Updated Blazor WebAssembly globalization support

.NET 5 Preview 8 reintroduces globalization support for Blazor WebAssembly based on International Components for Unicode (ICU). Part of introducing the ICU data and logic is optimizing these payloads for download size. This work is not fully completed yet. We expect to reduce the size of the ICU data in future .NET 5 preview updates.

New InputRadio Blazor component

Blazor in .NET 5 now includes built-in InputRadio and InputRadioGroup components. These components simplify data binding to radio button groups with integrated validation alongside the other Blazor form input components.

Opinion about blazor:
<InputRadioGroup @bind-Value="survey.OpinionAboutBlazor">
    @foreach (var opinion in opinions)
    {
        <div class="form-check">
            <InputRadio class="form-check-input" id="@opinion.id" Value="@opinion.id" />
            <label class="form-check-label" for="@opinion.id">@opinion.label</label>
        </div>
    }
</InputRadioGroup>

Set UI focus in Blazor apps

Blazor now has a FocusAsync convenience method on ElementReference for setting the UI focus on that element.

<button @onclick="() => textInput.FocusAsync()">Set focus</button>
<input @ref="textInput"/>

Control Blazor component instantiation

You can now control how Blazor components are instantiated by providing your own IComponentActivator service implementation.

Thank you Mladen Macanović for this Blazor feature contribution!

Influencing the HTML head in Blazor apps

Use the new Title, Link, and Meta components to programmatically set the title of a page and dynamically add link and meta tags to the HTML head in a Blazor app.

To use the new Title, Link, and Meta components:

  1. Add a package reference to the Microsoft.AspNetCore.Components.Web.Extensions package.
  2. Include a script reference to _content/Microsoft.AspNetCore.Components.Web.Extensions/headManager.js.
  3. Add a @using directive for Microsoft.AspNetCore.Components.Web.Extensions.Head.

The following example programmatically sets the page title to show the number of unread user notifications, and updates the page icon a as well:

@if (unreadNotificationsCount > 0)
{
    var title = $"Notifications ({unreadNotificationsCount})";
    <Title Value="title"></Title>
    <Link rel="icon" href="icon-unread.ico" />
}

Protected browser storage

In Blazor Server apps, you may want to persist app state in local or session storage so that the app can rehydrate it later if needed. When storing app state in the user’s browser, you also need to ensure that it hasn’t been tampered with.

Blazor in .NET 5 helps solve this problem by providing two new services: ProtectedLocalStorage and ProtectedSessionStorage. These services help you store state in local and session storage respectively, and they take care of protecting the stored data using the ASP.NET Core data protection APIs.

To use the new protected browser storage services:

  1. Add a package reference to Microsoft.AspNetCore.Components.Web.Extensions.
  2. Configure the services by calling services.AddProtectedBrowserStorage() from Startup.ConfigureServcies.
  3. Inject either ProtectedLocalStorage and ProtectedSessionStorage into your component implementation:
    @inject ProtectedLocalStorage ProtectedLocalStorage
    @inject ProtectedSessionStorage ProtectedSessionStorage
    
  4. Use the desired service to get, set, and delete state asynchronously:
    private async Task IncrementCount()
    {
        await ProtectedLocalStorage.SetAsync("count", ++currentCount);
    }
    

Model binding and validation with C# 9 record types

You can now use C# 9 record types with model binding in an MVC controller or a Razor Page. Record types are a great way to model data being transmitted over the wire.

For example, the PersonController below uses the Person record type with model binding and form validation:

public record Person([Required] string Name, [Range(0, 150)] int Age);

public class PersonController
{
   public IActionResult Index() => View();

   [HttpPost]
   public IActionResult Index(Person person)
   {
          // ...
   }
}

Person/Index.cshtml

@model Person

Name: <input asp-for="Model.Name" />
<span asp-validation-for="Model.Name" />

Age: <input asp-for="Model.Age" />
<span asp-validation-for="Model.Age" />

Improvements to DynamicRouteValueTransformer

ASP.NET Core in .NET Core 3.1 introduced DynamicRouteValueTransformer as a way to use use a custom endpoint to dynamically select an MVC controller action or a razor page. In .NET 5 Preview 8 you can now pass state to your DynamicRouteValueTransformer and filter the set of endpoints chosen.

Auto refresh with dotnet watch

In .NET 5, running dotnet watch on an ASP.NET Core project will now both launch the default browser and auto refresh the browser as you make changes to your code. This means you can open an ASP.NET Core project in your favorite text editor, run dotnet watch run once, and then focus on your code changes while the tooling handles rebuilding, restarting, and reloading your app. We expect to bring the auto refresh functionality to Visual Studio in the future as well.

Console Logger Formatter

We’ve made improvements to the console log provider in the Microsoft.Extensions.Logging library. Developers can now implement a custom ConsoleFormatter to exercise complete control over formatting and colorization of the console output. The formatter APIs allow for rich formatting by implementing a subset of the VT-100 (supported by most modern terminals) escape sequences. The console logger can parse out escape sequences on unsupported terminals allowing you to author a single formatter for all terminals.

JSON Console Logger

In addition to support for custom formatters, we’ve also added a built-in JSON formatter that emits structured JSON logs to the console. You can switch from the default simple logger to JSON, add to following snippet to your Program.cs:

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
+       .ConfigureLogging(logging =>
+       {
+           logging.AddJsonConsole(options =>
+           {
+               options.JsonWriterOptions = new JsonWriterOptions() { Indented = true };
+           });
+       })
        .ConfigureWebHostDefaults(webBuilder =>
        {
            webBuilder.UseStartup<Startup>();
        });

Once enabled, log messages emitted to the console are now JSON formatted.

{
  "EventId": 0,
  "LogLevel": "Information",
  "Category": "Microsoft.Hosting.Lifetime",
  "Message": "Now listening on: https://localhost:5001",
  "State": {
    "Message": "Now listening on: https://localhost:5001",
    "address": "https://localhost:5001",
    "{OriginalFormat}": "Now listening on: {address}"
  }
}

Give feedback

We hope you enjoy this release of ASP.NET Core in .NET 5! We are eager to hear about your experiences with this latest .NET 5 release. Let us know what you think by filing issues on GitHub.

Thanks for trying out ASP.NET Core!

52 comments

Discussion is closed. Login to edit/delete existing comments.

    • Daniel RothMicrosoft employee 0

      Looks like you are correct. The issue corresponding issue was mislabeled as being done in Preview 8, but this was actually done for the upcoming RC1 release. I’ve removed this content from the blog. Thanks for pointing this out!

  • D Smith 0

    one big problem there is only one version of webassembly and it’s W3C standard and its been around since 2017, its not multithreaded and being W3C is not likely to any change any time soon if ever (still the March 2017 release). and there is the fact its hosted a browser sandbox so you anything a JS applet can not to mention the bloat of BYOF , I’m not saying it totally useless but the overhyping is out of control, anything can interact with a Web service, so it cant do anything special , the notion that you could do something even a simple WPF app could is total BS, anyone considering basing somthing on blazor should take a close look at the all the “futures” MS has bamboozled hapless novices into the last few years

  • Rod Macdonald 0

    Hi Daniel, any news about CSS is good news! I have a WebForms dinosaur project to move forwards, still waiting for better control support from 3rd party vendors and AoT for a part of my solution to make the transition to Blazor. Can I please resolve something in my mind, and that is, does Blazor rely upon or interoperate with bootstrap, both .js and .css wise? Logic would suggest that it might support the bootstrap.css bit with C# switching out the .js bit. Thank you for any clarification.

    • Daniel RothMicrosoft employee 0

      Hi Rod. Blazor apps by default make use of the Bootstrap CSS, and you can use JS interop to leverage the Boostrap JS logic. There are various community libraries that do the latter. Checkout https://aka.ms/awesome-blazor for a list of Blazor community projects.

      • Rod Macdonald 0

        Many thanks Daniel, appreciated. I should also have asked if CSS Grid is another way forwards. I assume anything CSS is going to work with Blazor and I’m quite surprised by how powerful Grid is.

        [STOP PRESS: there’s a resource on the page you mention titled “Blazor CSS Grid layout library” so looking good!]

        • Daniel RothMicrosoft employee 0

          Yup, you can use any CSS features you want. Blazor is based on standard HTML & CSS, but enables you to write your logic in C# instead of JS.

  • Christophe Peugnet 0

    Great developments!
    I almost tested everything but I’m stuck on ProtectedLocalStorage or ProtectedSessionStorage.
    I did add the package “Microsoft.AspNetCore.Components.Web.Extensions v5.0.0-preview.8.20414.8“,
    add in startup.csservices.AddProtectedBrowserStorage();“,
    add in my component :

    @inject ProtectedLocalStorage protectedLocalStorage 
    @inject ProtectedSessionStorage protectedSessionStorage 

    but by example I have this error :
    The type or namespace name ‘protectedSessionStorage ‘ could not be found
    An idea ?

    • Daniel RothMicrosoft employee 0

      Hi Christophe. It sounds like the injected properties are not getting generated correctly. Are perhaps missing a @using directive for the Microsoft.AspNetCore.Components.ProtectedBrowserStorage namespace?

      • Christophe Peugnet 0

        Hi Daniel ! Thank you for your time. You’re right :
        I forgot to had in _Imports.razo : @using Microsoft.AspNetCore.Components.Web.Extensions

  • Lorenzo Melato 0

    This is a great release. We are planning to test the lazy loading features in the next few days.
    There’s some news in the roadmap about the AOT compilation?

    • Daniel RothMicrosoft employee 0

      Hi Lorenzo. Support for AoT compilation to WebAssembly is planned for .NET 6.

  • Peter Row 0

    Not strictly related to the details in this post but perhaps someone who’s been trying the previews can answer.

    From a video I watched about the .NET 5 where it mentions the unification it sounded very much like there will be no more .NET Core and .NET full framework there will just be .NET 5 >>> 6 >>> 7 etc…

    So if I was running a MVC app on full fat .NET 4.5.2 and I wanted to upgrade it to .NET 5.0, am I right in saying that the amount of effort involved will be the same as going from 4.5.2 to .NET Core 3.x now? i.e. a ton of effort because of the differences between old full fat framework vs. .NET Core, i.e. .NET 5.0 is .NET Core + more APIs migrated?

    • Daniel RothMicrosoft employee 0

      So if I was running a MVC app on full fat .NET 4.5.2 and I wanted to upgrade it to .NET 5.0, am I right in saying that the amount of effort involved will be the same as going from 4.5.2 to .NET Core 3.x now? i.e. a ton of effort because of the differences between old full fat framework vs. .NET Core, i.e. .NET 5.0 is .NET Core + more APIs migrated?

      Correct, .NET 5 is really the next version of .NET Core, so you should expect the level of compatibility with .NET Framework to be similar to what it is with .NET Core today.

  • Everson Abreu 0

    Very good, I am very excited about Blazor no. NET 5. The new feature to focus on an input opens up some possibilities, which perhaps the Blazor team may consider in the future, such as: Obtaining the cursor position and the selected content within an input, something that could be provided in an ElementReference instance.

  • Ben Hayat 0

    Dan, is there plan to be able to compile a Blazor app to Web Assembly format, so Blazor app will run natively in Web Assembly as if it was written in C++ or other native format for .Net 6?
    This should help in 3 areas:
    1) Smaller overall size
    2) Faster execution
    3) Faster reactivity to user interaction.

    Thanks
    .Ben

    • Daniel RothMicrosoft employee 0

      Hi Ben. Yes, ahead-of-time (AoT) compilation of Blazor apps to WebAssembly is planned for .NET 6. This should significantly improve runtime performance, but most likely at the expense of increased download size, since .NET IL is generally more compact than its natively compiled form.

  • JinShil 0

    I could really use the ability to pass a handle to a Javascript object to .Net code in Blazor Web Assembly. Thanks!

    • Daniel RothMicrosoft employee 0

      Hi JinShil. We just finished adding support for JSObjectReferences, which should be available in the next .NET 5 preview update. Stay tuned!

      • JinShil 0

        Wow! That’s a pleasant surprise. Thank you, and nice work!

Feedback usabilla icon