- •Table of Contents
- •About the Author
- •Acknowledgments
- •Introduction
- •Version Support
- •Supported Versions
- •A Unified Platform
- •Roadmap
- •Supported Operating Systems
- •Command Line Interface
- •Desktop Development
- •Blazor
- •MAUI
- •Wrapping Up
- •.NET 6 Architecture
- •Runtimes
- •CoreCLR
- •Mono
- •WinRT
- •Managed Execution Process
- •Desktop Packs
- •Wrapping Up
- •Dotnet New
- •Dotnet Restore
- •NuGet.config
- •Dotnet Build
- •Dotnet Publish
- •Dotnet Run
- •Dotnet Test
- •Using the CLI in GitHub Actions
- •Other Commands
- •Wrapping Up
- •WinAPI
- •WinForms
- •STAThread
- •WinForms Startup
- •DPI Mode
- •Responding to Scale Events
- •Visual Styles
- •Text Rendering
- •The Message Loop
- •The Form Designer
- •WPF Startup
- •XAML Layout
- •Visual Tree
- •Data Binding
- •Windows App SDK
- •Building a Windows App SDK application
- •Using Windows APIs with Windows App SDK
- •Packaging
- •Migrating to .NET 6
- •Upgrade Assistant
- •Wrapping Up
- •Blazor WebAssembly
- •Creating a Blazor Wasm Project
- •Blazor Progressive Web Apps
- •Exploring the Blazor Client Project
- •Blazor in .NET 6
- •Blazor Component System
- •Creating Blazor Pages
- •Running a Blazor App
- •Blazor Server
- •SignalR
- •Blazor Desktop
- •Wrapping Up
- •Project Structure
- •Exploring MAUI
- •The Cross-Platform World
- •Application Lifecycle
- •MVVM
- •MVVM Toolkit
- •Wrapping Up
- •Model-View-Controller
- •Routing
- •Views
- •Controllers
- •Controller-Based APIs
- •Minimal APIs
- •Wrapping Up
- •Web Apps
- •Creating an App Service
- •Static Web Apps
- •Web App for Containers
- •Docker
- •Azure Functions
- •Deploying Azure Functions
- •Wrapping Up
- •Record Types
- •Monolith Architecture
- •Microservices
- •Container Orchestration
- •Kubernetes
- •Docker Compose
- •Dapr
- •Installing Dapr
- •Dapr State Management
- •Wrapping Up
- •Roslyn
- •Compiler API
- •Diagnostic API
- •Scripting API
- •Workspace API
- •Syntax Tree
- •Roslyn SDK
- •Source Generators
- •Writing a Source Generator
- •Debugging Source Generators
- •Wrapping Up
- •Garbage Collector
- •The Heap
- •The Stack
- •Garbage Collection
- •A Look at the Threadpool
- •Async in .NET 6
- •Await/Async
- •Cancellations
- •WaitAsync
- •Conclusion
- •Index
Chapter 5 Blazor
A total of 9.7 MB was downloaded; this was the first launch of that specific application. Figure 5-10 shows the same metric, but after launching the app for a second time.
Figure 5-10. Launching the app again
Launching the app again takes significantly less time and resources. That is because the browser caches as much as possible. There’s even quite a big chance that your users won’t notice the three seconds it takes to load your application. That is because there is a big difference between speed and the perception of speed. As mentioned before, the first thing loaded into the browser is the index.html file. That file contains logic to show “Loading…,” while the WASM and .NET runtimes are being downloaded and started; replace that with a nice loading animation, and your application will be perceived as loading quite fast.
Blazor Server
Blazor Server is a second flavor of Blazor. It looks and feels very similar to Blazor WASM, but the big difference is in the underlying architecture. Instead of running inside web assembly, it actually runs on a server, hence the name. Blazor server uses a SignalR connection to send requests to a server that handles all the instructions and sends back changes to the DOM. Before we go any deeper, let’s see what SignalR is.
SignalR
SignalR has been around for quite some years. It is a framework that allows developers to easily implement real-time communication between clients and servers. It enables server code to directly call methods on clients instead of clients having to poll for data on the server. A simple example of this is a chat application where client A pushes
a new message to the server; the server then calls client B with the new message as parameter. All of this is possible due to Websockets, which is the underlying mechanism of SignalR. But it goes one step further. On platforms that don’t support Websockets,
144
Chapter 5 Blazor
SignalR can automatically fall back to older transport protocols. Because of this fallback functionality and a load of abstractions on top of the Websockets, API SignalR has quickly gained a lot of popularity.
Thanks to SignalR we have server–client and client–server communication. By leveraging this, Microsoft built a server-based web framework that does not do page reloads; instead, they receive a piece of DOM through the SignalR connection. We can see this in action by launching the counter page of the default Blazor Server template. The project template is mostly the same as Blazor WASM, from App.razor to MainLayout down to the code of the components. Just like in Blazor WASM, when you inspect the HTML code and click the counter button, you will see that only the element containing the number is updated; the rest of the page is never reloaded.
There are a few differences in how Blazor Server launches compared to Blazor WASM. Let’s start at the main entrance point of the application, Program.cs.
Listing 5-12. Program.cs from a Blazor Server application
var builder = WebApplication.CreateBuilder(args);
// Add services to the container. builder.Services.AddRazorPages(); builder.Services.AddServerSideBlazor(); builder.Services.AddSingleton<WeatherForecastService>();
var app = builder.Build();
// Configure the HTTP request pipeline. if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
//The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
145
Chapter 5 Blazor
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.MapBlazorHub(); app.MapFallbackToPage("/_Host");
app.Run();
Program.cs in .NET 6 contains top-level statements once again, making for a cleaner file without a class declaration. If you’ve used ASP.NET MVC before, the instructions in this file might look very familiar. That is because the SignalR connection is powered by ASP.NET, so we need to bootstrap that framework as well. Similar to Blazor WASM, we start by registering services in the built-in dependency injection (DI). After registering services, we have basic boilerplate code to enable HTTPS, set up routing, and start
the app.
There are two Blazor-specific calls in this file. The first one is builder. Services.AddServerSideBlazor();. This call registers Blazor-specific services into the DI container, services like the NavigationManager for navigation between Razor components or the IJSRuntime to enable JavaScript interop. The exact code of this method can be found on GitHub https://github.com/dotnet/ aspnetcore/blob/main/src/Components/Server/src/DependencyInjection/
ComponentServiceCollectionExtensions.cs. The second Blazor-specific call is app. MapBlazorHub();. This call opens the connection to the Blazor SignalR hub. A SignalR hub is a class that contains communication logic between clients and a SignalR server. In this case, the hub will contain the logic that enables receiving instructions and data from a client, generating a DOM diff and sending it back to the client. The SignalR part of Blazor Server is also open sourced on GitHub at https://github.com/dotnet/ aspnetcore/tree/main/src/Components/Server/src/BlazorPack.
Another difference from Blazor WASM is the absence of the index.html page from the wwwroot folder; instead, there is a _Host.cshtml file in the Pages folder of the project. Files with the cshtml extension are ASP.NET webpages; since Blazor Server has need for a webserver that supports .NET, we can leverage the power of ASP.NET as well. The
_Host.cshtml file is mostly HTML 5 with a namespace and a routing attribute. But it does contain an interesting line that impacts the entire application.
146
Chapter 5 Blazor
Listing 5-13. Rendering mode in Blazor Server
<component type="typeof(App)" render-mode="ServerPrerendered" />
The component tag specifies where in the host file our actual Blazor application will get rendered. The type parameter sets the startup type of the app. The render mode will specify where the application is rendered and how dynamic it can be. There are three options.
•\ |
Static: All components are rendered into static HTML, meaning that |
|
there is no connection to a Blazor SignalR server and pages have no |
|
Blazor functionality. |
•\ |
Server: The webserver builds the HTML, connects to SignalR, and |
|
activates all Blazor functionality. After the server is finished with all |
|
that, the browser will receive the HTML and render everything. This |
|
is the slowest option but with the most consistent results. |
•\ |
ServerPrerendered: This option uses a technique called hydration. |
|
Hydration is a known pattern in most popular SPA frameworks; it |
|
takes the best of both static and server render modes to find a middle |
|
ground between performance and functionality. Hydration is a two- |
|
step process; the first step renders static HTML which gives users |
|
the illusion of a fast page load. At this point, the page has appeared |
|
on screen but there is no Blazor functionality. In the second step, |
|
there is a piece of JavaScript in blazor.server.js that will open the |
|
connection to the SignalR hub and hydrate the already rendered page |
|
with functionality; the page basically re-renders invisible to the user. |
|
The blazor.server.js file is a file included in the Microsoft.AspNetCore. |
|
Components.Server.dll assembly. It gets injected in your application’s |
|
output automatically. |
ServerPrerendered is the default option, and looking from an end-user perspective, it is the most interesting one performance vs. functionality-wise. However, do be careful with automated tests. We have run into issues where the test runner is clicking a button before the re-rendering has taken place. The re-rendering usually happens fast, from a human perspective. Automated tests are executed by machines and can happen faster than the re-rendering.
147