Posted on Leave a comment

.NET Day on Agentic Modernization Coming Soon

Join us on December 9, 2025 between 9AM-1PM Pacific for .NET Day of Agentic Modernization! This is a free, one-day virtual event focused on the latest tooling, techniques, and guidance for modernizing your .NET applications. Whether you’re upgrading legacy code, preparing for cloud workloads, or exploring how AI and agentic patterns fit into your architecture, this event will show you what’s possible with today’s tooling while keeping reliability, security, and developer control front and center. Buckle up because we’ll be demo heavy and you can get your questions answered live!

.NET Day on Agentic Modernization event banner

Agenda

We have 8 great sessions throughout the event that will be broadcast live. Tune in and get your questions answered from the presenters!

🚀 Choose Your Modernization Adventure with GitHub Copilot with Brady Gaster – See how GitHub Copilot and Visual Studio speed app modernization- upgrading code, fixing dependencies, and guiding secure, cloud-ready migrations into Azure.

⚙️ Agentic DevOps: Enhancing .NET Web Apps with Azure MCP with Yun Jung Choi – Learn how AI-powered tooling and Azure MCP streamline .NET app development-code, storage, SQL, and IaC workflows-with faster, smarter Azure-ready delivery.

🛡️ Fix It Before They Feel It: Proactive .NET Reliability with Azure SRE Agent with Deepthi Chelupati and Shamir Abdul Aziz – See how Azure SRE Agent and App Insights detect .NET regressions, automate rollbacks, and streamline incident prevention with custom agents and health checks.

☁️ No‑Code Modernization for ASP.NET with Managed Instance on Azure App Service with Andrew Westgarth and Gaurav Seth – See how Azure App Service Managed Instance removes ASP.NET migration blockers-enabling fast modernization, better performance, lower TCO, and integration with modern agentic workflows.

🤖 Modernization Made Simple: Building Agentic Solutions in .NET with Bruno Capuano – Learn how to add the Agent Framework to existing .NET apps – unlocking multi-agent collaboration, memory, and tool orchestration with practical, fast-start guidance.

💪 Bulletproof Agents with the Durable Task Extension for Microsoft Agent Framework with Chris Gillum and Thiago Almeida – See how the Durable Extension for the Microsoft Agent Framework brings durable, distributed, deterministic, and debuggable AI agents to Azure—enabling reliable, scalable, production-ready agentic workflows.

🔐 Securely Unleash AI Agents on Azure SQL and SQL Server with Davide Mauri – Learn how to let AI agents work safely with Azure SQL – enforcing strict security, least-privilege access, and schema-aware techniques that prevent data leaks and query errors.

Secure and Smart AI Agents Powered by Azure Redis with Catherine Wang – See how Azure Redis powers secure, streamlined data access for .NET agentic apps – using MCP, Redis-backed tools, and modern security patterns to simplify development.

Tune in

Don’t miss this opportunity to get practical, real-world guidance on modernizing .NET applications for Azure, AI, and agentic patterns. Mark your calendars and get ready for .NET Day on Agentic Modernization!

Posted on Leave a comment

What’s new for the WinForms Visual Basic Application Framework

Klaus Loeffelmann

Melissa Trevino

.NET from version .NET Core 3.1 up to .NET 7 has plenty of advantages over .NET
Framework: it provides performance improvements in almost every area, and those
improvements were an ongoing effort over each .NET version. The
latest improvements in .NET
6
and
.NET
7
are
really worth checking out.

Migrating your Windows Forms (WinForms) Visual Basic Apps to .NET 6/7+ also
allows to adopt modern technologies which are not (or are no longer) supported in .NET
Framework. EFCore is one example: it is
a modern Entity Framework data access technology that enables .NET developers to
work with database backends using .NET objects. Although it is not natively
supported for VB by Microsoft, it is designed in a way that it is easy for the
community to build up on it and provide code generation support for additional
languages like Visual Basic
. In
that context there are also changes and improvements in the new WinForms
Out-of-Process Designer for .NET, especially around Object Data
Sources
.
For the WinForms .NET runtime, there are a series of improvements in different
areas which have been introduced with the latest releases of .NET:

The new Visual Basic Application Framework Experience

In contrast to the project property Application Framework Designer experience in
earlier versions of Visual Studio and for .NET Framework, you will noticed that
the project properties UI in Visual Studio has changed. It’s style is now in
parity with the project properties experience for other .NET project types: we
have invested into modernizing the experience for developers, focusing on
enhancing productivity and a modern look and feel.

Screenshot of the new Visual Basic Application Framework project settings designer.

We’ve added theming and search to the new experience. If this is your first time
you’re working with the new project properties experience in Visual Studio, it’s
a good idea to read up on the the introductory
blog
.

In contrast to C# projects, Visual Basic Application Framework projects use a
special file for storing the Application Framework project settings: the
Application.myapp file. We’ll talk more about the technical details of how
this file connects the project settings to the VB project specific code
generation of the My namespace later, but one thing to keep in mind is how the
UI translates each property’s value to this file:

  • Windows Visual Styles is to determine if the application will use the most
    current version for the Control Library comctl.dll to provide control
    rendering with modern visual styling. This setting translates to the value
    EnableVisualStyles of type Boolean inside of Application.myapp.

  • Single-instance application is to determine if the application will prevent
    users from running multiple instances of the application. This setting is
    switched off by default, which allows multiple instances of the application to
    be run concurrently. This setting translates to the value SingleInstance of
    type Boolean.

  • Save user settings on exit is to determine if the application settings are
    automatically saved when an app is about to shut down. The settings can be
    changed with the settings editor. In contrast to .NET Framework, a new Visual
    Basic Application Framework App doesn’t contain a settings file by default,
    but you can easily insert one over the project properties, should you need
    one, and then manage the settings
    interactively
    .

    Screenshot of the Settings section in the Application Framework project's property pages

    Adding to the list of settings automatically generates respective code, which
    can be easily access over the My object in the Visual Basic Application
    Framework at
    runtime
    .
    This settings translates to the value SaveMySettingsOnExit of type
    Boolean.

  • High DPI mode is to identify the application-wide HighDpiMode for the
    application. Note that this setting can be programmatically overridden through
    the HighDpiMode
    property

    of the ApplyApplicationDefaultsEventArgs of the ApplyApplicationDefaults
    application event. Choose from the following setting:

    • DPI unaware (0): The application window does not scale for DPI changes and
      always assumes a scale factor of 100%. For higher resolutions, this will
      make text and fine drawings more blurry, but may impose the best setting for
      some apps which demand a high backwards compatibility in rendering content.
    • DPI unaware GDI scaled (4): similar to DPI unaware, but improves the
      quality of GDI/GDI+ based on content. Please note that this mode will not
      work as expected, when you have enabled double
      buffering

      for control rendering via OnPaint and related functionality.
    • Per monitor (2): Per-Monitor DPI allows individual displays to have their
      own DPI scaling setting. WinForms doesn’t optimize for this mode, and
      Per-Monitor V2 should be used instead.
    • Per monitor V2 (3): Per-Monitor V2 offers more advanced scaling features
      such as improved support for mixed DPI environments, improved display
      enumeration, and support for dynamically scaling on-client area of windows.
      In WinForms common controls are optimized for this high dpi mode. Please
      note the events
      Form.DpiChange,
      Control.DpiChangedAfterParent
      and
      Control.DpiChangeBeforeParent,
      when your app need to scale up or down content based on a changed DPI
      environment, for example, when the user of your app has dragged a Form from
      one monitor to another monitor with a different DPI setting.
    • System aware (1): The application queries for the DPI of the primary
      monitor once and uses this for the application on all monitors. When content
      in Forms is dragged from one monitor to another with a different HighDPI
      setting, content might become blurry. SystemAware is WinForm’s most
      compatible high-dpi rendering mode for all supported controls.
  • Authentication mode is to specify the method of identifying the logged-on
    user, when needed. The setting translates to the value AuthenticationMode as
    an enum value of type Integer:

    • 0: The WindowsFormsApplicationBase(AuthenticationMode) constructor does
      not automatically initialize the principal for the application’s main
      thread. It’s completely the developer’s task, to manage authentication for
      the user.
    • 1: The WindowsFormsApplicationBase(AuthenticationMode) constructor
      initializes the principal for the application’s main thread with the current
      user’s Windows user info.
  • Shutdown mode is to to indicate which condition causes the application to
    shut down. This setting translates to the value ShutdownMode as an enum
    value of type Integer (Note: Please also refer to the application event
    ShutDown
    and the further remarks down below.):

    • 0: When the main form closes.
    • 1: Only after the last form closes.
  • Splash screen represents the name of the form to be used as a splash screen
    for the application. Note that the file name does not need to include the
    extension (.vb). This setting translates to the value SplashScreen of type
    String.

    Note: you will may be missing the settings for the Splash dialog up to
    Visual Studio 2022 version 17.5. For a workaround, read the comments in
    the section “A look behind the scenes”. To recap: a “Splash” dialog is
    typically displayed for a few seconds when an application is launched.
    Visual Basic has an item template which you can use to add a basic splash
    dialog to your project. It usually displays the logo or name of the
    application, along with some kind of animation or visual effects, to give
    users the impression that the application is loading or initializing. The
    term “splash” in this context is used because the dialog is designed to
    create a splash or impact on the user, drawing their attention to the
    application while it loads.

  • Application Framework is saved both in the Application.myapp file and the
    .vbproj file:

    • Application.myapp saves the setting MySubMain of type Boolean to
      identify if the Application Framework is enabled.
    • .vbproj uses the setting MyType for identifying the usage of the
      Application Framework for a VB project. If the Application Framework is
      enabled, the value is WindowsForms; if the Application Framework is
      disabled, the value is WindowsFormsWithCustomSubMain.
  • Startup object is the name of the form that will be used as the entry
    point, without its filename extension. Note: this property is found in the
    project property Settings under the General section, and not in the
    Application Framework section. This setting translates to the value MainForm of type
    String, when the Application Framework is activated. The start object setting in
    the .vbproj file is ignored in that case – see also the comments below on this
    topic.

Custom constants new look

Screenshot of the new custom constants editor in the project properties UI.

We are introducing a new custom constants-control in the modernized Project
Property Pages for VB Projects, that allows to encode the input to the format
key=”value”. Our goal is that users will be able to input their custom constants
in a more streamlined key-value pair format, thus enhancing their productivity.
Feedback is welcomed – if you have any comments or suggestions, feel free to
reach out to the project system
team
by filing a new issue or
comment on existing ones.

A look behind the scenes of the WinForms VB Application Framework

The way basic properties and behaviors of a WinForms app are controlled and configured is fundamentally different between C# and Visual Basic. In C#, every app
starts with a static method called main which can usually be found in a file
called Program.cs, and in that main method all the setting get applied.

That is different in Visual Basic. Since VB Apps in WinForms are based on the
Application Framework runtime, there are a few features, which aren’t
intrinsically available to C# WinForms apps to begin with, like configuring to
automatically show Splash dialogs (see below) or ensure a single instance
application start. Since you configure most of the parts of your app
interactively in VB with the settings described above at design time, the actual
code which honors or ensures those settings later at runtime is mostly
code-generated and somewhat hidden behind the scenes. The starting point of a VB
app is therefore not so obvious. There are also a series of differences in .NET
Visual Basic apps when it comes to hooking up event code which is supposed to
run, for example when a VB WinForms app starts, ends, or runs into an unhandled
exception – just to name a few examples.

That all said, technically Visual Basic doesn’t break any fundamental rules.
Under the hood, there is of course a Shared Sub Main when you activate the
Application Framework. You just do not write it yourself, and you don’t see it,
because it is generated by the VB compiler and then automatically added to your
Start Form. This is done by activating the VB compiler switch
/main.

At the same time, when you are activating the Application Framework, a series of
conditional compiler constants are defined. One of the constants is called
_mytype. If that constant is defined as Windows then the VB compiler
generates all the necessary infrastructure code to support the Application
Framework. If that constant is defined as WindowsFormsWithCustomSubMain
however, the VB compiler just generates the bare minimum infrastructure code and
doesn’t apply any settings to the WinForms app on startup. The latter happens,
when you deactivate the Application Framework. This setting is stored in the
vbproj project file, along with the Start Form. What’s important to know
though in this context: only in the case of WindowsFormsWithCustomSubMain, so
with the Application Framework deactivated, is the Start Form definition
actually taken from the vbproj file. When the Application Framework is
activated however then that is the case when the aforementioned
Application.myapp file is used as the settings container. Note, that by
default you cannot find that file in the solution explorer.

Screenshot of solution explorer showing the Application.myapp file.

You need to make sure first to show all files for that project (see screenshot
above). Then you can open the My Project-folder and show that setting file in
the editor by double-clicking it in the solution explorer. The content of that
file looks something like this:

<?xml version="1.0" encoding="utf-16"?>
<MyApplicationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"> <MySubMain>true</MySubMain> <MainForm>Form1</MainForm> <SingleInstance>false</SingleInstance> <ShutdownMode>0</ShutdownMode> <EnableVisualStyles>true</EnableVisualStyles> <AuthenticationMode>0</AuthenticationMode> <SaveMySettingsOnExit>true</SaveMySettingsOnExit> <HighDpiMode>3</HighDpiMode>
</MyApplicationData>

Note: Visual Studio 2022 before version 17.6 (Preview 3) won’t have the
option to pick a Splash Dialog interactively, as mentioned above. We will have
an interactive designer for setting the splash form only from that version on
on. Up to then, you can manually patch the Application.myapp file to trigger
the code generation for the Splash dialog. Insert the following line of code in
that file and save the changes.

<SplashScreen>SplashDialog</SplashScreen>

When you do this, make sure not to include the filename extension (.vb) in
that definition, because otherwise the required code does not get generated.

Application.myapp as the source for code generation

Now, if you take a closer look at that file’s properties in the property
browser, you’ll see that it is triggering a custom tool which is invoked
whenever that file is saved.

Screenshot of solution explorer showing the properties for the Application.myapp file.

And that custom tool generates VB code which you
can find under the Application.myapp node in the Solution Explorer in
Application.Designer.vb. It does the following:

  • It defines a Friend Partial Class MyApplication. With the Application
    Framework enabled, that class is inherited from
    WindowsFormsApplicationBase.
    You don’t see that Inherits statement here and the reason is that the major
    part of that Class’
    definition

    is injected by the Visual Basic compiler based on the earlier defined
    conditional constant _myapp.
  • It generates the code to apply all the settings which were saved in
    Application.myapp file.
  • It creates code for a method which overrides
    OnCreateMainForm.
    In that method, it assigns the Form, which is defined as the start form in the
    Application.myapp file.

Warning: The Application.Designer.vb is not supposed to be edited, as it’s
auto-generated. Any changes will be lost as soon as you make changes to Application.myapp. Instead, use the project properties UI.

Now, the class which is injected by the compiler is also responsible for
generating everything which the Visual Basic Application Framework provides you
via the My namespace. The My namespace simplifies access to frequently used
information about your WinForms app, your system, or simplifies access to
frequently used APIs. Part of the My namespace for an activated Application
Framework is the Application property, and its return type is of exactly that
type which is defined by the class generated based on your Application Settings
and then merged with the injected Visual Basic compiler file mentioned earlier.
So, if you access My.Application you are basically accessing a single instance
of the My.MyApplication type which the generated code defines.

With this context understood, we can move on to how two additional features of
the Application Framework work and can be approached. The first one is extending
the My namespace with additional function areas. We won’t go too much into
them, because there are detailed docs about the My namespace and how to
extend
it
.

An even more important concept to understand are the Application Events which are
provided by the Application Framework. Since there isn’t a good way to intercept
the startup or shut down of an app (since that code gets generated
and sort of hidden inside the main Form) Application Events are the way to be
notified of certain application-global occurrences.

Note in this context, that there is a small breaking change in the UI: while in
.NET Framework, you had to insert a code file named ApplicationEvents.vb via
the Property Settings of the VB project, in a .NET Core App this file will be
there from the start when you’ve created a new Application Framework project.

To wire up the available Application events, you open that ApplicationEvent.vb
code file, and then you select ApplicationEvents from the Object drop-down list,
and the application event you want to write up from the events list:

Animated gif showing how to wire app Application Events in the ApplicationEvent.vb code file

As you can see, the ApplicationEvent.vb code file again extends the MyApplication class – this time by the events handler you place there on demand. The options you have here are:

  • Startup: raised when the application starts, before the start form is created.
  • Shutdown: raised after all application forms are closed. This event is not raised if the application terminates abnormally.
  • UnhandledException: raised if the application encounters an unhandled exception.
  • StartupNextInstance: raised when launching a single-instance application and the application is already active.
  • NetworkAvailabilityChanged: raised when the network connection is connected or disconnected.
  • ApplyApplicationDefaults: raised when the application queries default values to be set for the application.

Note: More general information about the Visual Basic Application Model is provided through the Microsoft Learn Docs about this topic. Also note, that, on top of the extensibility of the My namespace, this Application Model also has extensibility points which are also described in great detail by the respective docs.

Summary

With the new and modernized project properties pages, WinForm’s Application
Framework is ready for new, .NET 6,7,8+ based Visual Basic Apps to develop. It’s
also the right time to think about modernizing your older .NET Framework based
VB Apps and bring them over to .NET 6,7,8+. WinForms and the .NET runtime
deliver countless new features and provide considerable performance improvements
for your apps in almost every area. Visual Basic and the Visual Basic
Application Framework are and continue to be first class citizens and are fully
supported in WinForms. Our plans are to continue modernizing around the VB App
Framework in the future without breaking code for existing projects.

And, as always: Feedback about the subject matter is really important to us, so
please let us know your thoughts and additional ideas! Please also note that the
WinForms .NET and the Visual Basic Application Framework runtime is open source,
and you can contribute! If you have general feature ideas, encountered bugs, or
even want to take on existing issues around the WinForms runtime and submit PRs,
have a look at the WinForms Github repo.
If you have suggestions around the WinForms Designer feel free to file new
issues there as well.

Happy coding!

Posted on Leave a comment

Announcing ASP.NET Core in .NET 5

Daniel Roth

Daniel

.NET 5 is now released! .NET 5 is the next version of .NET Core and the future of the .NET platform. With .NET 5 you have everything you need to build rich, interactive front end web UI and powerful backend services.

ASP.NET Core in .NET 5 is loaded with lots of great new features and improvements. Here’s a sampling:

For a full list of everything that’s new in ASP.NET Core in this release, check out the ASP.NET Core in .NET 5 release notes. See also the .NET 5 release notes for additional details and known issues.

Get started

To get started with ASP.NET Core in .NET 5, install the .NET 5 SDK. .NET 5 is also included with the latest updates to Visual Studio and Visual Studio for Mac, so if you’ve already updated your IDE, then you should be good to go.

Visual Studio 2019 16.8 or later is required to use .NET 5 from Visual Studio. .NET 5 is also supported with 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 Core 3.1 to .NET 5:

To upgrade an existing ASP.NET Core app from .NET 5 RC2 to .NET 5:

  • Update all Microsoft.AspNetCore.*, Microsoft.Extensions.*, and System.* package references to 5.0.0.

That’s it! You should be all set to enjoy the benefits of .NET 5.

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

Performance improvements

With every .NET release a special effort is made to improve all aspects of the performance in ASP.NET Core applications. ASP.NET Core in .NET 5 is the fastest .NET web framework we’ve ever shipped!

.NET 5 contains great performance improvements in the runtime and libraries and for the gRPC components. These improvements, when applied to ASP.NET Core, result in some significant wins in throughput (RPS) and latency. The performance results below are taken from the TechEmpower benchmarks for ASP.NET Core with .NET Core 3.1 and from the latest results for ASP.NET Core with .NET 5.

Troughput (max, in RPS)

Scenario 3.1 5.0 Change
Plaintext Platform 8,593 11,690 +36.0%
Json Platform 872 1,261 +44.6%
Fortunes Platform 344 423 +23.0%
Plaintext 3,993 4,420 +12.4%
Json 743 912 +22.6%
Fortunes 223 259 +16.2%

Latency (mean, in ms)

Scenario 3.1 5.0 Change
Plaintext Platform 1.52 0.82 -46.3%
Json Platform 1.24 0.41 -66.7%
Fortunes Platform 1.57 1.26 -19.7
Plaintext 0.87 0.83 -4.2%
Json 1.52 1.20 -21.1%
Fortunes 1.11 0.87 -21.6

The “platform” tests represent highly optimized benchmark implementations, while the non-platform tests use more typical framework components.

The TechEmpower performance composite score, an index based on all of the TechEmpower benchmarks aggregated together, increased from 5,659 to 6,626 with .NET 5, which represents a 17% increase.

Deploy .NET 5 Web Apps to Azure App Service Today

The ASP.NET and Azure App Service teams have worked tirelessly together throughout the .NET 5 timeline to make sure ASP.NET developers have access to the latest bits of the .NET 5 runtime on day zero. This means that, as soon as you can download .NET 5 and get started building apps with it, you can deploy those .NET 5 apps to App Service using Visual Studio and Visual Studio Code, and you’ll have full support for deploying .NET 5 apps to App Service using GitHub Actions.

This day-zero availability of .NET 5 on App Service will be made possible by their new Early Access Runtime feature, which will enable not only .NET 5 apps, but every subsequent release of .NET from now on. This means no more waiting for the platform to support the runtime – it’ll “just be there” as soon as the runtime is available. You can learn more about the Azure App Service Early Access Runtime technology on the App Service team’s blog.

Whilst Early Access will give you a supported path to deploying your .NET 5 apps to Azure App Service immediately, there are some slight nuances to how Early Access works with which you can familiarize yourself on the App Service Early Access .NET page.

Join us for the .NET 5 release at .NET Conf 2020

Come celebrate with us and learn all about the .NET 5 release at .NET Conf 2020, a FREE, three day virtual developer event co-organized by the .NET community and Microsoft. This year marks the 10th anniversary of the conference, and it will be better than ever with over 80 live sessions from speakers in the community and from the Microsoft product teams. The conference starts today and goes from November 10-12. We hope you can join us!

Thank you!

Thank you to everyone in the community who helped make this release of .NET 5 possible! This release represents the culmination of many GitHub issues, pull requests, design feedback comments and documentation updates contributed by many members of the .NET community. We couldn’t have made it to this point without you!

We hope you enjoy this release of ASP.NET Core in .NET 5. We are eager to hear about your experiences with this latest release. Let us know what you think on GitHub by filing new issues and by commenting on or expressing your support (👍) for existing ones.

Thanks again for trying out ASP.NET Core!

Posted on Leave a comment

Unified Blazor UI in the Mobile Blazor Bindings Preview 5

Eilon Lipton

Eilon

I’m excited to announce that today we are releasing the Mobile Blazor Bindings Preview 5 update that adds support for sharing UI between web apps and mobile/desktop apps. You can now use a Razor Class Library (RCL) to build your UI and app logic once and use it in a Blazor Web app and in a Mobile Blazor Bindings app. This release also includes many other improvements, such as support for Shell with Blazor @page routing, SkiaSharp for rich graphics, gesture recognizers, and a whole lot more!

You can build one UI using Blazor Web and host it in a Blazor Server or Blazor Web Assembly app and also in a Mobile Blazor Bindings hybrid app to target Android, iOS, macOS, and Windows:

Image cat tracker options

These are the major new features in the 0.5 Preview 5 release:

  • Build your UI in a Razor Class Library (RCL) and share the same UI between web app and native app, including static assets such as CSS and images (community contribution from Jan-Willem Spuij)
  • Support for Shell with Routing, including the Razor @page directive (community contribution from Lachlan Gordon
  • Support for SkiaSharp graphics (community contribution from Lachlan Gordon
  • Gesture recognizers (tap, swipe, etc.)
  • Dual-screen device support
  • Added FontImageSource, DatePicker, TimePicker
  • Simplified Grid layout syntax
  • Update to Xamarin.Forms 4.8
  • Nightly package feed
  • OSS docs on GitHub
  • And several bug fixes and other small improvements
  • The full list of closed issues is here: https://github.com/xamarin/MobileBlazorBindings/milestone/5?closed=1

Get started

To get started building a Blazor Hybrid app with Experimental Mobile Blazor Bindings preview 5, install the .NET Core 3.1 SDK, have Visual Studio with the Mobile development with .NET (Xamarin.Forms) and ASP.NET and web development workloads installed, and then run the following command:

dotnet new -i Microsoft.MobileBlazorBindings.Templates::0.5.50-preview

And then create your first project by running this command:

dotnet new blazorhybrid -o MyHybridApp

Note: For additional information on required software, please check the Getting Started article.

Now open it in Visual Studio and run it on Android, iOS, Windows, or macOS. That’s it! You can find additional docs and tutorials on https://docs.microsoft.com/mobile-blazor-bindings/. We have a new tutorial for building a Razor Class Library and including it in a Blazor Server app as well as targeting Android, iOS, macOS, and Windows platforms.

Upgrade an existing project

To update an existing Mobile Blazor Bindings project please refer to the Migrate Mobile Blazor Bindings From Preview 4 to Preview 5 topic for full details.

Razor Class Library support

Razor Class Libraries are a feature of Razor that enable you to package part of your application’s UI and logic into a reusable library. This library can then be reused in multiple web applications. Now in Mobile Blazor Bindings you can use the same library in a Blazor Hybrid application as part of a mobile or desktop app. The web content is hosted in a Web View, just like any hybrid app content, and it can interact with any native parts of the application, and the reverse is true as well. Previous versions included limited support for RCLs, and this version adds improved support for them, particularly support for serving static assets such as CSS and images, as well as support for JSInterop for interop between JavaScript and .NET code.

Shell navigation

To build a great native experience for your app you can use the Shell control that comes from Xamarin.Forms. Starting with Preview 5 you can author native Blazor pages with the @page "/search/{query}" syntax and integrate them with the Shell’s navigation (such as the Back button) and navigate to them using the new NavigationManager.NavigateToAsync($"/search/{query}") API. Pages can use the navigation manager by injecting it from the DI container as in @inject ShellNavigationManager NavigationManager.

SkiaSharp graphics

SkiaSharp is a popular cross-platform graphics library based on Google’s Skia graphics library. In Preview 5 you can directly use SkiaSharp’s Canvas APIs to render rich high-performance graphics directly in your Mobile Blazor Bindings app.

Here’s what a simple canvas might look like:

<ContentView> <StackLayout Padding="20"> <SKCanvasView OnPaintSurface="PaintSurface"/> </StackLayout>
</ContentView> @code
{ void PaintSurface(SKPaintSurfaceEventArgs e) { var canvas = e.Surface.Canvas; canvas.Clear(SKColors.Green); var paint = new SKPaint { Color = SKColors.SkyBlue, }; canvas.DrawLine(0, 0, 200, 200, paint); }
}

And here’s what a more complex canvas might render as:

SkiaSharp rendering a graph in Mobile Blazor Bindings on iOS

More information

Thank you to contributors

This release had several major contributions from community contributors:

  • Jan-Willem Spuij added support for Razor Class Libraries with static assets. He stayed up late (his time) on calls with me several times and I appreciate his hard work!
  • Lachlan Gordon added support for Shell navigation and SkiaSharp. His patience in playing time-zone ping-pong (Australia vs. USA) is most appreciated and resulted in high quality contributions!

Thanks you to Jan-Willem, Lachlan, and everyone else who contributed to this release!

What’s next? Let us know what you want!

This project relies on your feedback to help shape the future of Blazor for native and hybrid scenarios. Please share your thoughts on this blog post or at the GitHub repo so we can keep the discussion going.

Posted on Leave a comment

gRPC performance improvements in .NET 5

Avatar

James

gRPC is a modern open source remote procedure call framework. There are many exciting features in gRPC: real-time streaming, end-to-end code generation, and great cross-platform support to name a few. The most exciting to me, and consistently mentioned by developers who are interested in gRPC, is performance.

Last year Microsoft contributed a new implementation of gRPC for .NET to the CNCF. Built on top of Kestrel and HttpClient, gRPC for .NET makes gRPC a first-class member of the .NET ecosystem.

In our first gRPC for .NET release, we focused on gRPC’s core features, compatibility, and stability. In .NET 5, we made gRPC really fast.

gRPC and .NET 5 are fast

In a community run benchmark of different gRPC server implementations, .NET gets the highest requests per second after Rust, and is just ahead of C++ and Go.

gRPC performance comparison

This result builds on top of the work done in .NET 5. Our benchmarks show .NET 5 server performance is 60% faster than .NET Core 3.1. .NET 5 client performance is 230% faster than .NET Core 3.1.

Stephen Toub discusses dotnet/runtime changes in his Performance Improvements in .NET 5 blog post. Check it out to read about improvements in HttpClient and HTTP/2.

In the rest of this blog post I’ll talk about the improvements we made to make gRPC fast in ASP.NET Core.

HTTP/2 allocations in Kestrel

gRPC uses HTTP/2 as its underlying protocol. A fast HTTP/2 implementation is the most important factor when it comes to performance. Our gRPC server builds on top of Kestrel, a HTTP server written in C# that is designed with performance in mind. Kestrel is a top contender in the TechEmpower benchmarks, and gRPC benefits from a lot of the performance improvements in Kestrel automatically. However, there are many HTTP/2 specific optimizations that were made in .NET 5.

Reducing allocations is a good place to start. Fewer allocations per HTTP/2 request means less time doing garbage collection (GC). And CPU time “wasted” in GC is CPU time not spent serving HTTP/2 requests.

.NET Core 3.1 memory graph

The performance profiler above is measuring allocations over 100,000 gRPC requests. The live object graph’s sawtooth shaped pattern indicates memory building up, then being garbage collected. About 3.9KB is being allocated per request. Lets try to get that number down!

dotnet/aspnetcore#18601 adds pooling of streams in a HTTP/2 connection. This one change almost cuts allocations per request in half. It enables reuse of internal types like Http2Stream, and publicly accessible types like HttpContext and HttpRequest, across multiple requests.

Once streams are pooled a range of optimizations become available:

There are many smaller allocation savings. dotnet/aspnetcore#19783 removes allocations in Kestrel’s HTTP/2 flow control. A resettable ManualResetValueTaskSourceCore<T> type replaces allocating a new object each time flow control is triggered. dotnet/aspnetcore#19273 replaces an array allocation with stackalloc when validating the HTTP request path. dotnet/aspnetcore#19277 and dotnet/aspnetcore#19325 eliminate some unintended allocations related to logging. dotnet/aspnetcore#22557 avoids allocating a Task<T> if a task is already complete. And finally dotnet/aspnetcore#19732 saves a string allocation by special casing content-length of 0. Because every allocation matters.

.NET 5 memory

Per-request memory in .NET 5 is now just 330 B, a decrease of 92%. The sawtooth pattern has also disappeared. Reduced allocations means garbage collection didn’t run at all while the server processed 100,000 gRPC calls.

Reading HTTP headers in Kestrel

A hotpath in HTTP/2 is reading and writing HTTP headers. A HTTP/2 connection supports concurrent requests over a TCP socket, a feature called multiplexing. Multiplexing allows HTTP/2 to make efficient use of connections, but only the headers for one request on a connection can be processed at a time. HTTP/2’s HPack header compression is stateful and depends on order. Processing HTTP/2 headers is a bottleneck so has to be as fast as possible.

dotnet/aspnetcore#23083 optimizes the performance of HPackDecoder. The decoder is a state machine that reads incoming HTTP/2 HEADER frames. The approach here is good, the state machine allows Kestrel to decode frames as they arrive, but the decoder was checking state after parsing each byte. Another problem is literal values, the header names and values, were copied multiple times. Optimizations in this PR include:

  • Tighten parsing loops. For example, if we’ve just parsed a header name then the value must come afterwards. There is no need to check the state machine to figure out the next state.
  • Skip literal parsing all together. Literals in HPack have a length prefix. If we know the next 100 bytes are a literal then there is no need to inspect each byte. Mark the literal’s location and resuming parsing at its end.
  • Avoid copying literal bytes. Previously literal bytes were always copied to an intermediary array before passed to Kestrel. Most of the time this isn’t necessary and instead we can just slice the original buffer and pass a ReadOnlySpan<byte> to Kestrel.

Together these changes significantly decrease the time it takes to parse headers. Header size is almost no longer a factor. The decoder marks the start and end position of a value and then slices that range.

private HPackDecoder _decoder = CreateDecoder();
private byte[] _smallHeader = new byte[] { /* HPack bytes */ };
private byte[] _largeHeader = new byte[] { /* HPack bytes */ };
private IHttpHeadersHandler _noOpHandler = new NoOpHeadersHandler(); [Benchmark]
public void SmallDecode() => _decoder.Decode(_smallHeader, endHeaders: true, handler: _noOpHandler); [Benchmark]
public void LargeDecode() => _decoder.Decode(_largeHeader, endHeaders: true, handler: _noOpHandler);
Method Runtime Mean Ratio Allocated
SmallDecode .NET Core 3.1 111.20 ns 1.00 0 B
SmallDecode .NET 5.0 71.90 ns 0.65 0 B
LargeDecode .NET Core 3.1 49,083.00 ns 1.00 0 B
LargeDecode .NET 5.0 98.68 ns 0.002 0 B

Once headers have been decoded, Kestrel needs to validate and process them. For example, special HTTP/2 headers like :path and :method need to be set onto HttpRequest.Path and HttpRequest.Method, and other headers need to be converted to strings and added to the HttpRequest.Headers collection.

Kestrel has the concept of known request headers. Known headers are a selection of commonly occuring request headers that have been optimized for fast setting and getting. dotnet/aspnetcore#24730 adds an even faster path for setting HPack static table headers to the known headers. The HPack static table gives 61 common header names and values a number ID that can be sent instead of the full name. A header with a static table ID can use the optimized path to bypass some validation and quickly be set in the collection based on its ID. dotnet/aspnetcore#24945 adds extra optimization for static table IDs with a name and value.

Adding HPack response compression

Prior to .NET 5, Kestrel supported reading HPack compressed headers in requests, but it didn’t compress response headers. The obvious advantage of response header compression is less network usage, but there are performance benefits as well. It’s faster to write a couple of bits for a compressed header than it is to encode and write the header’s full name and value as bytes.

dotnet/aspnetcore#19521 adds initial HPack static compression. Static compression is pretty simple: if the header is in the HPack static table then write the ID to identify the header instead of the longer name.

Dynamic HPack header compression is more complicated, but also provides bigger gains. Response header names and values are tracked in a dynamic table and are each assigned an ID. As a response’s headers are written, the server checks to see if the header name and value are in the table. If there is a match then the ID is written. If there isn’t then the full header is written, and it is added to the table for the next response. There is a maximum size of the dynamic table, so adding a header to it may evict other headers with a first in, first out order.

dotnet/aspnetcore#20058 adds dynamic HPack header compression. To quickly search for headers the dynamic table groups header entries using a basic hash table. To track order and evict the oldest headers, entries maintain a linked list. To avoid allocations, removed entries are pooled and reused.

Wireshark HTTP/2 response

Using Wireshark, we can see the impact of header compression on response size for this example gRPC call. .NET Core 3.x writes 77 B, while .NET 5 is only 12 B.

Protobuf message serialization

gRPC for .NET uses the Google.Protobuf package as the default serializer for messages. Protobuf is an efficient binary serialization format. Google.Protobuf is designed for performance, using code generation instead of reflection to serialize .NET objects. There are some modern .NET APIs and features that can be added to it to reduce allocations and improve efficiency.

The biggest improvement to Google.Protobuf is support for modern .NET IO types: Span<T>, ReadOnlySequence<T> and IBufferWriter<T>. These types allow gRPC messages to be serialized directly using buffers exposed by Kestrel. This saves Google.Protobuf allocating an intermediary array when serializing and deserializing Protobuf content.

Support for Protobuf buffer serialization was a multi-year effort between Microsoft and Google engineers. Changes were spread across multiple repositories.

protocolbuffers/protobuf#7351 and protocolbuffers/protobuf#7576 add support for buffer serialization to Google.Protobuf. This is by far the biggest and most complicated change. Three attempts were made to add this feature before the right balance between performance, backwards compatibility and code reuse was found. Protobuf reading and writing uses many performance oriented features and APIs added to C# and .NET Core:

  • Span<T> and C# ref struct types enables fast and safe access to memory. Span<T> represents a contiguous region of arbitrary memory. Using span lets us serialize to managed .NET arrays, stack allocated arrays, or unmanaged memory, without using pointers. Span<T> and .NET protects us against buffer overflow.
  • stackalloc is used to create stack-based arrays. stackalloc is a useful tool to avoid allocations when a small buffer is required.
  • Low-level methods such as MemoryMarshal.GetReference(), Unsafe.ReadUnaligned() and Unsafe.WriteUnaligned() convert directly between primitive types and bytes.
  • BinaryPrimitives has helper methods for efficiently converting between .NET primitive types and bytes. For example, BinaryPrimitives.ReadUInt64LittleEndian reads little endian bytes and returns an unsigned 64 bit number. Methods provided by BinaryPrimitive are heavily optimized and use vectorization.

A great thing about modern C# and .NET is it is possible to write fast, efficient, low-level libraries without sacrificing memory safety. When it comes to performance, .NET lets you have your cake and eat it too!

private TestMessage _testMessage = CreateMessage();
private ReadOnlySequence<byte> _testData = CreateData();
private IBufferWriter<byte> _bufferWriter = CreateWriter(); [Benchmark]
public IMessage ToByteArray() => _testMessage.ToByteArray(); [Benchmark]
public IMessage ToBufferWriter() => _testMessage.WriteTo(_bufferWriter); [Benchmark]
public IMessage FromByteArray() => TestMessage.Parser.ParseFrom(CreateBytes()); [Benchmark]
public IMessage FromSequence() => TestMessage.Parser.ParseFrom(_testData);
Method Runtime Mean Ratio Allocated
ToByteArray .NET 5.0 1,133.82 ns 1.00 184 B
ToBufferWriter .NET 5.0 589.05 ns 0.51 64 B
FromByteArray .NET 5.0 409.88 ns 1.00 1960 B
FromSequence .NET 5.0 381.03 ns 0.92 1776 B

Adding support for buffer serialization to Google.Protobuf is just the first step. More work is required for gRPC for .NET to take advantage of the new capability:

  • grpc/grpc#18865 and grpc/grpc#19792 adds ReadOnlySequence<byte> and IBufferWriter<byte> APIs to the gRPC serialization abstraction layer in Grpc.Core.Api.
  • grpc/grpc#23485 updates gRPC code generation to glue the changes in Google.Protobuf to Grpc.Core.Api.
  • grpc/grpc-dotnet#376 and grpc/grpc-dotnet#629 updates gRPC for .NET to use the new serialization abstractions in Grpc.Core.Api. This code is the integration between Kestrel and gRPC. Because Kestrel’s IO is built on top of System.IO.Pipelines, we can use its buffers during serialization.

The end result is gRPC for .NET serializes Protobuf messages directly to Kestrel’s request and response buffers. Intermediary array allocations and byte copies have been eliminated from gRPC message serialization.

Wrapping Up

Performance is a feature of .NET and gRPC, and as cloud apps scale it is more important than ever. I think all developers can agree it is fun to make fast apps, but performance has real world impact. Lower latency and higher throughput means fewer servers. It is an opportunity to save money, reduce power use and build greener apps.

.NET Core 3.1 vs .NET 5 results

As is obvious from this tour, a lot of changes have gone into gRPC, Protobuf and .NET aimed at improving performance. Our benchmarks show a 60% improvement in gRPC server RPS and a 230% improvement in gRPC client RPS.

.NET 5 RC2 is available now, and the official .NET 5 release is in November. To try out the performance improvements and to get started using gRPC with .NET, the best place to start is the Create a gRPC client and server in ASP.NET Core tutorial.

We look forward to hearing about apps built with gRPC and .NET, and to your future contributions in the dotnet and grpc repos!

Posted on Leave a comment

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

Daniel Roth

Daniel

.NET 5 Release Candidate 2 (RC2) is now available and is ready for evaluation. .NET 5 RC2 is a “go live” release, meaning it’s supported in production. This prerelease of .NET 5 is very close to what we expect to ship for the .NET 5 release.

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

  • CSS isolation improvements
  • Blazor WebAssembly debugging improvements
  • Browser platform compatibility tooling

ASP.NET Core in .NET 5 contains lots of great new functionality and improvements! The list below summarizes the many improvements we’ve made in ASP.NET Core for .NET 5 that you can try out in this release:

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

Get started

To get started with ASP.NET Core in .NET 5 RC2, install the .NET 5 SDK. .NET RC2 also is included with Visual Studio 2019 16.8 Preview 4.

Visual Studio 2019 16.8 Preview 4 or later is required to use .NET 5 RC2 from Visual Studio. .NET 5 RC2 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 RC1 to .NET 5 RC2:

  • Update all Microsoft.AspNetCore.* package references to 5.0.0-rc.2.*.
  • Update all Microsoft.Extensions.* package references to 5.0.0-rc.2.*.
  • Update System.Net.Http.Json package references to 5.0.0-rc.2.*.
  • Update Microsoft.AspNetCore.Components.Web.Extensions package references to 5.0.0-preview9.20513.1.
  • Remove any package references to Microsoft.AspNetCore.Components.ProtectedBrowserStorage
  • Update Microsoft.AspNetCore.Components.ProtectedBrowserStorage namespace to Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage.
  • Remove unnecessary service registrations for ProtectedLocalStorage and ProtectedSessionStorage.
  • Rename JSObjectReference to IJSObjectReference.
  • In Blazor apps, replace CSS references to _framework/scoped.styles.css and _content/{project_name}/_framework/scoped.styles.css with {project_name}.styles.css.

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?

Blazor CSS isolation improvements

In .NET 5 Preview 8 we introduced support for CSS isolation for Blazor components. Based on user feedback, we’ve made a number of improvements to CSS isolation in this release.

Previously, all component scoped CSS files including files from referenced projects or packages were compiled into a single bundle, scoped.styles.css. We now produce one bundle per referenced project or package and include those bundles into the app bundle through CSS @import statements.

The bundle names are now based on the project names: {project_name}.styles.css. Each bundle can be referenced from the root path of the app by default. This makes the path of the app bundle the same for both Blazor Server and Blazor WebAssembly projects:

<link href="BlazorApp1.styles.css" rel="stylesheet" />

Component specific styles can also now use normal wwwroot-relative paths to refer to related assets, like images. We’ve updated Razor Class Library template to make use of component specific styles following this pattern.

Component1.razor.css

.my-component { border: 2px dashed red; padding: 1em; margin: 1em 0; background-image: url('background.png');
}

We also fixed some issues with how scoped CSS styles get built so that changes are correctly picked up with each build in Visual Studio instead of requiring a full rebuild.

Blazor WebAssembly debugging improvements

.NET 5 includes a variety of improvements to Blazor WebAssembly debugging:

  • Various reliability improvements, including fixing the port conflict issue from RC1
  • Improved support for stepping over and out of async methods
  • Inspect locals or object properties in many previously unsupported situations:
    • For inherited members
    • For multicast delegates
    • For boxed values
    • For Nullable<T> values
    • Within reflection based calls
  • Support for debugging lazy loaded assemblies

Browser platform compatibility tooling

The core framework libraries in .NET 5 have now been annotated to indicate which APIs are supported in browser scenarios. The platform compatibility analyzer uses this data to give appropriate warnings when using APIs from a Blazor WebAssembly app that are not supported when running in a browser on WebAssembly.

Browser compatibility check

Learn more about how you can use the new platform compatibility analyzer to discover cross-platform compatibility issues.

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!

Posted on Leave a comment

Azure Static Web Apps with .NET and Blazor

aapowell

Aaron

Azure Static Web Apps is a service that automatically builds and deploys full stack web apps from a GitHub repository. Azure Static Web Apps consist of a static web frontend, and an Azure Functions based backend. When you create a Static Web Apps resource, Azure sets up a GitHub Actions workflow in the app’s source code repository that monitors a branch of your choice. Every time you push commits to the watched branch, the GitHub Action automatically builds and deploys your app and its API.

As of today, Azure Static Web Apps now has first-class support for Blazor WebAssembly and .NET Functions in preview, available in all supported regions. This was one of the top user requests since Static Web Apps was announced at Build. You can develop and deploy a frontend and a serverless API written entirely in .NET.

Let’s give it a try!

Getting Started

To help you get started, we’ve created a GitHub repository template, which you can use as a starting point for your own projects.

In GitHub, click on the Use this template button to create a new GitHub repository using the template, providing it a name of your choosing (we’ll use my-blazor-app here) and click Create repository from template.

There are three folders in the template:

  • Client: The Blazor WebAssembly sample application
  • API: A C# Azure Functions API, which the Blazor application will call
  • Shared: A C# class library with a shared data model between the Blazor and Functions application

To run the app locally, start both the API and Client projects. The “Fetch data” page in the Blazor app requests weather forecast data form the backend Function API and display it on the page.

To run the app from the command-line, you’ll need to install the Azure Functions Core Tools for your platform of choice.

Deploying to Static Web Apps

To deploy this app as an Azure Static Web App, log into your Azure account (sign up for a free one here) and search for Static Web Apps.

Static Web Apps in the portal

Click Create, provide a Subscription, Resource Group and name for the application.

Next, sign into GitHub and locate your GitHub repo (my-blazor-app) and select the branch you wish to deploy.

Finally, select Blazor from the Build Presets, which will populate the App location, API location and App artifact location with Client, API and wwwroot. The first and second values are the path within the Git repository to where the project files for the Blazor and Functions app resides, so if you’ve modified the structure of the Git repository, make sure these values are updated to reflect. The third value is the output path that Blazor will compile into and doesn’t need updating.

Azure Static Web App wizard

Complete the wizard and Static Web Apps will create the GitHub Actions workflow file for you and deploy your application to Azure.

gif of Action completing and then going to the portal to open the deployed app

Summary

We started by forking a GitHub repository (but you could’ve also used a project created in Visual Studio/Visual Studio Code/etc.), created a Static Web App in Azure, and it setup a GitHub Actions workflow for us, automatically building and deploying our Blazor and Functions application. We’ve now got a statically hosted application with a serverless backend, ready to scale for our demands.

Additional Resources

To learn more about Blazor WebAssembly and .NET Functions on Static Web Apps, check out the quickstart docs, the Microsoft Learn module, as well as the comprehensive Static Web Apps docs.

Give Feedback

We’re excited to have Blazor WebAssembly and .NET Functions supported by Azure Static Web Apps, and can’t wait to see what you build with them. Let us know what you think by filing an issue on GitHub.

Posted on Leave a comment

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

Daniel Roth

Daniel

.NET 5 Release Candidate 1 (RC1) is now available and is ready for evaluation. Here’s what’s new in this release:

  • Blazor WebAssembly performance improvements
  • Blazor component virtualization
  • Blazor WebAssembly prerendering
  • Browser compatibility analyzer for Blazor WebAssembly
  • Blazor JavaScript isolation and object references
  • Blazor file input support
  • Custom validation class attributes in Blazor
  • Blazor support for ontoggle event
  • Model binding DateTime as UTC
  • Control Startup class activation
  • Open API Specification (Swagger) on-by-default in ASP.NET Core API projects
  • Better F5 Experience for ASP.NET Core API Projects
  • SignalR parallel hub invocations
  • Added Messagepack support in SignalR Java client
  • Kestrel endpoint-specific options via configuration

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

Get started

To get started with ASP.NET Core in .NET 5 RC1 install the .NET 5 SDK. .NET RC1 also is included with Visual Studio 2019 16.8 Preview 3.

You need to use Visual Studio 2019 16.8 Preview 3 or newer to use .NET 5 RC1. .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 8 to .NET 5 RC1:

  • Update all Microsoft.AspNetCore.* package references to 5.0.0-rc.1.*.
    • If you’re using the new Microsoft.AspNetCore.Components.Web.Extensions package, update to version 5.0.0-preview.9.*. This package doesn’t have an RC version number yet because we expect to make a few more design changes to the components it contains before it’s ready to ship.
  • Update all Microsoft.Extensions.* package references to 5.0.0-rc.1.*.
  • Update System.Net.Http.Json package references to 5.0.0-rc.1.*.

In Blazor WebAssembly projects, also make the follwowing updates to the project file:

  • Update the SDK from “Microsoft.NET.Sdk.Web” to “Microsoft.NET.Sdk.BlazorWebAssembly”
  • Remove the <RuntimeIdentifier>browser-wasm</RuntimeIdentifier> and <UseBlazorWebAssembly>true</UseBlazorWebAssembly> properties.

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?

Blazor WebAssembly performance improvements

For .NET 5, we’ve made significant improvements to Blazor WebAssembly runtime performance, with a specific focus on complex UI rendering and JSON serialization. In our performance tests, Blazor WebAssembly in .NET 5 is 2-3x faster for most scenarios.

Runtime code execution in Blazor WebAssembly in .NET 5 is generally faster than Blazor WebAssembly 3.2 due to optimizations in the core framework libraries and improvements to the .NET IL interpreter. Things like string comparisons, dictionary lookups, and JSON handling are generally much faster in .NET 5 on WebAssembly.

As shown in the chart below, JSON handling is almost twice as fast in .NET 5 on WebAssembly:

Blazor WebAssembly 1kb JSON handling

We also optimized the performance of Blazor component rendering, particularly for UI involving lots of components, like when using high-density grids.

To test the performance of grid component rendering in .NET 5, we used three different grid component implementations, each rendering 200 rows with 20 columns:

  • Fast Grid: A minimal, highly optimized implementation of a grid
  • Plain Table: A minimal but not optimized implementation of a grid.
  • Complex Grid: A maximal, not optimized implementation of a grid, using a wide range of Blazor features at once, deliberately intended to create a bad case for the renderer.

From our tests, grid rendering is 2-3x faster in .NET 5:

Blazor WebAssembly grid rendering

You can find the code for these performance tests in the ASP.NET Core GitHub repo.

You can expect ongoing work to improve Blazor WebAssembly performance. Besides optimizing the Blazor WebAssembly runtime and framework, the .NET team is also working with browser implementers to further speed up WebAssembly execution. And for .NET 6, we expect to ship support for ahead-of-time (AoT) compilation to WebAssembly, which should further improve performance.

Blazor component virtualization

You can further improve the perceived performance of component rendering using the new built-in virtualization support. Virtualization is a technique for limiting the UI rendering to just the parts that are currently visible, like when you have a long list or table with many rows and only a small subset is visible at any given time. Blazor in .NET 5 adds a new Virtualize component that can be used to easily add virtualization to your components.

A typical list or table-based component might use a C# foreach loop to render each item in the list or each row in the table, like this:

@foreach (var employee in employees)
{ <tr> <td>@employee.FirstName</td> <td>@employee.LastName</td> <td>@employee.JobTitle</td> </tr>
}

If the list grew to include thousands of rows, then rendering it may take a while, resulting in a noticeable UI lag.

Instead, you can replace the foreach loop with the Virtualize component, which only renders the rows that are currently visible.

<Virtualize Items="employees" Context="employee"> <tr> <td>@employee.FirstName</td> <td>@employee.LastName</td> <td>@employee.JobTitle</td> </tr>
</Virtualize>

The Virtualize component calculates how many items to render based on the height of the container and the size of the rendered items.

If you don’t want to load all items into memory, you can specify an ItemsProvider, like this:

<Virtualize ItemsProvider="LoadEmployees" Context="employee"> <tr> <td>@employee.FirstName</td> <td>@employee.LastName</td> <td>@employee.JobTitle</td> </tr>
</Virtualize>

An items provider is a delegate method that asynchronously retrieves the requested items on demand. The items provider receives an ItemsProviderRequest, which specifies the required number of items starting at a specific start index. The items provider then retrieves the requested items from a database or other service and returns them as an ItemsProviderResult<TItem> along with a count of the total number of items available. The items provider can choose to retrieve the items with each request, or cache them so they are readily available.

async ValueTask<ItemsProviderResult<Employee>> LoadEmployees(ItemsProviderRequest request)
{ var numEmployees = Math.Min(request.Count, totalEmployees - request.StartIndex); var employees = await EmployeesService.GetEmployeesAsync(request.StartIndex, numEmployees, request.CancellationToken); return new ItemsProviderResult<Employee>(employees, totalEmployees);
}

Because requesting items from a remote data source might take some time, you also have the option to render a placeholder until the item data is available.

<Virtualize ItemsProvider="LoadEmployees" Context="employee"> <ItemContent> <tr> <td>@employee.FirstName</td> <td>@employee.LastName</td> <td>@employee.JobTitle</td> </tr> </ItemContent> <Placeholder> <tr> <td>Loading...</td> </tr> </Placeholder>
</Virtualize>

Blazor WebAssembly prerendering

The component tag helper now supports two additional render modes for prerendering a component from a Blazor WebAssembly app:

  • WebAssemblyPrerendered: Prerenders the component into static HTML and includes a marker for a Blazor WebAssembly app to later use to make the component interactive when loaded in the browser.
  • WebAssembly: Renders a marker for a Blazor WebAssembly app to use to include an interactive component when loaded in the browser. The component is not prerendered. This option simply makes it easier to render different Blazor WebAssembly components on different cshtml pages.

To setup prerendering in an Blazor WebAssembly app:

  1. Host the Blazor WebAssembly app in an ASP.NET Core app.
  2. Replace the default static index.html file in the client project with a _Host.cshtml file in the server project.
  3. Update the server startup logic to fallback to _Host.cshtml instead of index.html (similar to how the Blazor Server template is set up).
  4. Update _Host.cshtml to use the component tag helper to prerender the root App component:

    <component type="typeof(App)" render-mode="WebAssemblyPrerendered" />
    

You can also pass parameters to the component tag helper when using the WebAssembly-based render modes if the parameters are serializable. The parameters must be serializable so that they can be transferred to the client and used to initialize the component in the browser. If prerendering, you’ll also need to be sure to author your components so that they can gracefully execute server-side without access to the browser.

<component type="typeof(Counter)" render-mode="WebAssemblyPrerendered" param-IncrementAmount="10" />

In additional to improving the perceived load time of a Blazor WebAssembly app, you can also use the component tag helper with the new render modes to add multiple components on different pages and views. You don’t need to configure these components as root components in the app or add your own marker tags on the page – the framework handles that for you.

Browser compatibility analyzer for Blazor WebAssembly

Blazor WebAssembly apps in .NET 5 target the full .NET 5 API surface area, but not all .NET 5 APIs are supported on WebAssembly due to browser sandbox constraints. Unsupported APIs throw PlatformNotSupportedException when running on WebAssembly. .NET 5 now includes a platform compatibility analyzer that will warn you when your app uses APIs that are not supported by your target platforms. For Blazor WebAssembly apps, this means checking that APIs are supported in browsers.

We haven’t added all of the annotations yet to the core libraries in .NET 5 to indicate which APIs are not supported in browsers, but some APIs, mostly Windows specific APIs, are already annotated:

Platform compatibility analyzer

To enable browser compatibilty checks for libraries, add “browser” as a supported platform in your project file (Blazor WebAssembly projects and Razor class library projects do this for you):

<SupportedPlatform Include="browser" />

When authoring a library, you can optionally indicate that a particular API is not supported in browsers by applying the UnsupportedOSPlatformAttribute:

[UnsupportedOSPlatform("browser")]
private static string GetLoggingDirectory()
{ // ...
}

Blazor JavaScript isolation and object references

Blazor now enables you to isolate your JavaScript as standard JavaScript modules. This has a couple of benefits:

  1. Imported JavaScript no longer pollutes the global namespace.
  2. Consumers of your library and components no longer need to manually import the related JavaScript.

For example, the following JavaScript module exports a simple JavaScript function for showing a browser prompt:

export function showPrompt(message) { return prompt(message, 'Type anything here');
}

You can add this JavaScript module to your .NET library as a static web asset (wwwroot/exampleJsInterop.js) and then import the module into your .NET code using the IJSRuntime service:

var module = await jsRuntime.InvokeAsync<JSObjectReference>("import", "./_content/MyComponents/exampleJsInterop.js");

The “import” identifier is a special identifier used specifically for importing a JavaScript module. You specify the module using its stable static web asset path: _content/[LIBRARY NAME]/[PATH UNDER WWWROOT].

The IJSRuntime imports the module as a JSObjectReference, which represents a reference to a JavaScript object from .NET code. You can then use the JSObjectReference to invoke exported JavaScript functions from the module:

public async ValueTask<string> Prompt(string message)
{ return await module.InvokeAsync<string>("showPrompt", message);
}

JSObjectReference greatly simplifies interacting with JavaScript libraries where you want to capture JavaScript object references, and then later invoke their functions from .NET.

Blazor file input support

Blazor now offers an InputFile component for handling file uploads, or more generally for reading browser file data into your .NET code.

The InputFile component renders as an HTML input of type “file”. By default, the user can select single files, or if you add the “multiple” attribute then the user can supply multiple files at once. When one or more files is selected by the user, the InputFile component fires an OnChange event and passes in an InputFileChangeEventArgs that provides access to the selected file list and details about each file.

<InputFile OnChange="OnInputFileChange" multiple /> <div class="image-list"> @foreach (var imageDataUrl in imageDataUrls) { <img src="@imageDataUrl" /> }
</div> @code { IList<string> imageDataUrls = new List<string>(); async Task OnInputFileChange(InputFileChangeEventArgs e) { var imageFiles = e.GetMultipleFiles(); var format = "image/png"; foreach (var imageFile in imageFiles) { var resizedImageFile = await imageFile.RequestImageFileAsync(format, 100, 100); var buffer = new byte[resizedImageFile.Size]; await resizedImageFile.OpenReadStream().ReadAsync(buffer); var imageDataUrl = $"data:{format};base64,{Convert.ToBase64String(buffer)}"; imageDataUrls.Add(imageDataUrl); } }
}

To read data from a user-selected file, you call OpenReadStream on the file and read from the returned stream. In a Blazor WebAssembly app, the data is streamed directly into your .NET code within the browser. In a Blazor Server app, the file data is streamed to your .NET code on the server as you read from the stream. In case you’re using the component to receive an image file, Blazor also provides a RequestImageFileAsync convenience method for resizing images data within the browser’s JavaScript runtime before they’re streamed into your .NET application.

Custom validation class attributes in Blazor

You can now specify custom validation class names in Blazor. This is useful when integrating with CSS frameworks, like Bootstrap.

To specify custom validation class names, create a class derived from FieldCssClassProvider and set it on the EditContext instance.

var editContext = new EditContext(model);
editContext.SetFieldCssClassProvider(new MyFieldClassProvider()); // ... class MyFieldClassProvider : FieldCssClassProvider
{ public override string GetFieldCssClass(EditContext editContext, in FieldIdentifier fieldIdentifier) { var isValid = !editContext.GetValidationMessages(fieldIdentifier).Any(); return isValid ? "legit" : "totally-bogus"; }
}

Blazor support for ontoggle event

Blazor now has support for the ontoggle event:

<div> @if (detailsExpanded) { <p>Read the details carefully!</p> } <details id="details-toggle" @ontoggle="OnToggle"> <summary>Summary</summary> <p>Detailed content</p> </details>
</div> @code { bool detailsExpanded; string message { get; set; } void OnToggle() { detailsExpanded = !detailsExpanded; }
}

Thank you Vladimir Samoilenko for this contribution!

Model binding DateTime as UTC

Model binding in ASP.NET Core now supports correctly binding UTC time strings to DateTime. If the request contains a UTC time string (for example https://example.com/mycontroller/myaction?time=2019-06-14T02%3A30%3A04.0576719Z), model binding will correctly bind it to a UTC DateTime without the need for further customization.

Control Startup class activation

We’ve provided an additional UseStartup overload that lets you provide a factory method for controlling Startup class activation. This is useful if you want to pass additional parameters to Startup that are initialized along with the host.

public class Program
{ public static async Task Main(string[] args) { var logger = CreateLogger(); var host = Host.CreateDefaultBuilder() .ConfigureWebHost(builder => { builder.UseStartup(context => new Startup(logger)); }) .Build(); await host.RunAsync(); }
}

Open API Specification On-by-default

Open API Specification is a industry-adopted convention for describing HTTP APIs and integrating them into complex business processes or with 3rd parties. Open API is widely supported by all cloud providers and many API registries, so developers who emit Open API documents from their Web APIs have a variety of new opportunities in which those APIs can be used. In partnership with the maintainers of the open-source project Swashbuckle.AspNetCore, we’re excited to announce that the ASP.NET Core API template in RC1 comes pre-wired with a NuGet dependency on Swashbuckle, a popular open-source NuGet package that emits Open API documents dynamically. Swashbuckle does this by introspecting over your API Controllers and generating the Open API document at run-time, or at build time using the Swashbuckle CLI.

In .NET 5 RC1, running dotnet new webapi will result in the Open API output being enabled by default, but if you prefer to have Open API disabled, use dotnet new webapi --no-openapi true. All .csproj files that are created for Web API projects will come with the NuGet package reference.

<ItemGroup> <PackageReference Include="Swashbuckle.AspNetCore" Version="5.5.1" />
</ItemGroup>

In addition to the NuGet package reference being in the .csproj file we’ve added code to both the ConfigureServices method in Startup.cs that activates Open API document generation.

public void ConfigureServices(IServiceCollection services)
{ services.AddControllers(); services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "WebApplication1", Version = "v1" }); });
}

The Configure method is also pre-wired to enlist in the Swashbuckle middleware. This lights up the document generation process and turns on the Swagger UI page by default in development mode. This way you’re confident that the out-of-the-box experience doesn’t accidentally expose your API’s description when you publish to production.

public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{ if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseSwagger(); app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "WebApplication1 v1")); } // ...
}

Azure API Management Import

When ASP.NET Core API projects are wired to output Open API in this way, the Visual Studio 2019 version 16.8 Preview 2.1 publishing experience will automatically offer an additional step in the publishing flow. Developers who use Azure API Management have an opportunity to automatically import their APIs into Azure API Management during the publish flow.

Publising APIs

This additional publishing step reduces the number of steps HTTP API developers need to execute to get their APIs published and used with Azure Logic Apps or PowerApps.

Better F5 Experience for Web API Projects

With Open API enabled by default, we were able to significantly improve the F5 experience for Web API developers. With .NET 5 RC1, the Web API template comes pre-configured to load up the Swagger UI page. The Swagger UI page provides both the documentation you’ve added for your API, but enables you to test your APIs with a single click.

Swagger UI page

SignalR parallel hub invocations

In .NET 5 RC1, ASP.NET Core SignalR is now capable of handling parallel hub invocations. You can change the default behavior and allow clients to invoke more than one hub method at a time.

public void ConfigureServices(IServiceCollection services)
{ services.AddSignalR(options => { options.MaximumParallelInvocationsPerClient = 5; });
}

Added Messagepack support in SignalR Java client

We’re introducing a new package, com.microsoft.signalr.messagepack, that adds messagepack support to the SignalR java client. To use the messagepack hub protocol, add .withHubProtocol(new MessagePackHubProtocol()) to the connection builder.

HubConnection hubConnection = HubConnectionBuilder.create("http://localhost:53353/MyHub") .withHubProtocol(new MessagePackHubProtocol()) .build();

Kestrel endpoint-specific options via configuration

We have added support for configuring Kestrel’s endpoint-specific options via configuration. The endpoint-specific configurations includes the Http protocols used, the TLS protocols used, the certificate selected, and the client certificate mode.

You are able to configure the which certificate is selected based on the specified server name as part of the Server Name Indication (SNI) extension to the TLS protocol as indicated by the client. Kestrel’s configuration also support a wildcard prefix in the host name.

The example belows shows you how to specify endpoint-specific using a configuration file:

{ "Kestrel": { "Endpoints": { "EndpointName": { "Url": "https://*", "Sni": { "a.example.org": { "Protocols": "Http1AndHttp2", "SslProtocols": [ "Tls11", "Tls12"], "Certificate": { "Path": "testCert.pfx", "Password": "testPassword" }, "ClientCertificateMode" : "NoCertificate" }, "*.example.org": { "Certificate": { "Path": "testCert2.pfx", "Password": "testPassword" } }, "*": { // At least one subproperty needs to exist per SNI section or it // cannot be discovered via IConfiguration "Protocols": "Http1", } } } } }
}

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!

Posted on Leave a comment

Free e-book: Blazor for ASP.NET Web Forms Developers

Avatar

Nish

We are thrilled to announce the release of our new e-book: Blazor for ASP.NET Web Forms developers. This book caters specifically to ASP.NET Web Forms developers looking for guidelines. As well as strategies for migrating their existing apps to a modern, open-source, and cross-platform web framework.

Blazor E-book for ASP.NET Web Forms

Blazor is a new web framework that changes what is possible when building web apps with .NET. It is also a client-side web UI framework based on C# instead of JavaScript. When paired with .NET running on the server, Blazor enables full-stack web development with .NET. Blazor shares many commonalities with ASP.NET Web Forms. Such as having a reusable component model and a simple way to handle user events. It also builds on the foundations of .NET Core to provide a modern and high-performance web development experience. Additionally, Blazor is a natural solution for ASP.NET Web Forms developers looking to take advantage of client-side development and the open-source, cross-platform future of .NET.

In this book, each Blazor concept is presented in the context of analogous ASP.NET Web Forms features and practices. The book covers:

  • Building Blazor apps.
  • How Blazor works.
  • Blazor’s relation to .NET Core.
  • Reasonable strategies for migrating existing ASP.NET Web Forms apps to Blazor where appropriate.
  • A reference sample that demonstrates the migration strategies used.

Blazor for ASP.NET Web Forms Developers Cover Image
Read Online

Should I Still Use ASP.NET Web Forms?

Of course! As long as the .NET Framework ships as part of Windows, ASP.NET Web Forms will be supported. For many Web Forms developers, the lack of cross-platform and open-source support is a non-issue. If you do not require cross-platform support, open-source, or any other new features in .NET Core or .NET 5, then stick with ASP.NET Web Forms on Windows. ASP.NET Web Forms will continue to be a productive way to write web apps for many years to come!

Other Free E-books

If you are in the path of migrating your existing web and server apps, check out our free e-books on gRPC for WCF Developers. As well as Migrating your .NET App to Azure and more at dot.net/architecture.

Feedback

The book and related reference application will be evolving, so we welcome your feedback! If you have comments about how this book can be improved, please submit feedback.

Posted on Leave a comment

ASP.NET Core updates in .NET 5 Preview 8

Daniel Roth

Daniel

.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
  • IAsyncDisposable for Blazor components
  • 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"/>

IAsyncDisposable support for Blazor components

Blazor components now support the IAsyncDisposable interface for the asynchronous release of allocated resources.

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:

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

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

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

<br />*Person/Index.cshtml* ```razor
@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!