© The Author(s), under exclusive license to APress Media, LLC, part of Springer Nature 2022
P. HimschootMicrosoft Blazorhttps://doi.org/10.1007/978-1-4842-7845-1_1

1. Introduction to WebAssembly and Blazor

Peter Himschoot1  
(1)
Melle, Belgium
 

I was attending the Microsoft Most Valued Professional and Regional Directors summit 2018 where we were introduced to Blazor for the first time by Steve Sanderson and Daniel Roth. And I must admit I was super excited about Blazor! Blazor is a framework that allows you to build Single-Page Applications (SPAs) using C# and allows you to run any standard .NET library in the browser. Before Blazor, your options for building a SPA were Angular, React, Vue.js (and others) using JavaScript, or one of the other higher-level languages like TypeScript (which gets compiled into JavaScript anyway). In this introduction, we will look at how browsers are now capable of running .NET assemblies in the browser using WebAssembly and Blazor.

A Tale of Two Wars

Think about it. The browser is one of the primary applications on your computer. You use it every day. Companies that build browsers know that very well and are bidding for you to use their browser. At the beginning of mainstream Internet, everyone was using Netscape, and Microsoft wanted a share of the market, so in 1995, they built Internet Explorer 1.0, released as part of Windows 95 Plus! pack.

The First Browser War

Newer versions were released rapidly, and browsers started to add new features such as <blink> and <marquee> elements. This was the beginning of the first browser war, giving people (especially designers) headaches because some developers were building pages with blinking marquee controls ../images/469993_3_En_1_Chapter/469993_3_En_1_Figf_HTML.gif. But developers were also getting sore heads because of incompatibilities between browsers. The first browser war was about having more HTML capabilities than the competition.

But all of this is now behind us with the introduction of HTML5 and modern browsers like Google Chrome, Microsoft Edge, Firefox, Safari, and Opera. HTML5 not only defines a series of standard HTML elements but also rules on how these should render, making it a lot easier to build a website that looks the same in all modern browsers. Then, in 1995, Brendan Eich wrote a little programming language known as ECMAScript (initially called LiveScript) in ten days (What!?). It was quickly dubbed JavaScript because its syntax was very similar to Java. I will be using the name JavaScript here because that is what most people call it.

JavaScript and Java are not related. Java and JavaScript have as much in common as ham and hamster (I don’t know who formulated this first, but I love this phrasing).

Little did Mr. Eich know how this language would impact the modern Web and even desktop application development. In 1995, Jesse James Garrett wrote a white paper called Ajax (Asynchronous JavaScript and XML), describing a set of technologies where JavaScript is used to load data from the server and that data is used to update the browser’s HTML. This avoids full page reloads and allows for client-side web applications, which are applications written in JavaScript that run completely in the browser. One of the first companies to apply Ajax was Microsoft when they built Outlook Web Access (OWA). OWA is a web application almost identical to the Outlook desktop application proving the power of Ajax. Soon other Ajax applications started to appear, with Google Maps stuck in my memory as one of the other keystone applications. Google Maps would download maps asynchronously and with some simple mouse interactions allowed you to zoom and pan the map. Before Google Maps, the server would do the map rendering and a browser displayed the map like any other image by downloading a bitmap from a server.

Building an Ajax website was a major undertaking that only big companies like Microsoft and Google could afford. This soon changed with the introduction of JavaScript libraries like jQuery and knockout.js (knockout was also written by Steve Sanderson, the author of Blazor!). Today, we build rich web apps with Angular, React, and Vue.js. All of them are using JavaScript or higher-level languages like TypeScript which gets transpiled into JavaScript.

Transpiling will take one language and convert it into another language. This is very popular with TypeScript which gives you a modern high-level typed language. You need JavaScript to run it in a browser, so TypeScript gets “transpiled” into JavaScript.

The Second Browser War

This brings us back to JavaScript and the second browser war. JavaScript performance is paramount in modern browsers. Chrome, Edge, Firefox, Safari, and Opera are all competing with one another, trying to convince users that their browser is the fastest with cool-sounding names for their JavaScript engine like V8 and Chakra. These engines use the latest optimization tricks like Just-In-Time (JIT) compilation where JavaScript gets converted into native code as illustrated in Figure 1-1.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig1_HTML.jpg
Figure 1-1

The JavaScript Execution Process

This process takes a lot of effort because JavaScript needs to be downloaded into the browser, where it gets parsed, then compiled into bytecode, and then Just-In-Time converted into native code. So how can we make this process even faster?

The second browser war is all about JavaScript performance.

Introducing WebAssembly

WebAssembly allows you to take the parsing and compiling to the server, before your users even open up their browser. With WebAssembly, you compile your code in a format called WASM (an abbreviation of WebASseMbly), which gets downloaded by the browser where it gets Just-In-Time compiled into native code as in Figure 1-2.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig2_HTML.jpg
Figure 1-2

The WebAssembly Execution Process

Open your browser and open https://earth.google.com. This should take you to the Google Earth app written in WebAssembly as shown in Figure 1-3. Play around with this a little bit and you will see that this application has excellent performance, but the initial load takes a fair amount of time because it needs to download the whole WASM application’s code.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig3_HTML.png
Figure 1-3

Google Earth in WebAssembly

What is WebAssembly? From the official site webassembly.org:

WebAssembly (abbreviated Wasm) is a binary instruction format for a stack-based virtual machine. Wasm is designed as a portable target for compilation of high-level languages like C/C++/Rust, enabling deployment on the web for client and server applications.

So WebAssembly as a new binary format optimized for browser execution, it is NOT JavaScript. It uses a stack-based virtual machine, just like .NET does. There are compilers for languages like C++ and Rust which compile to WASM. Some people have compiled C++ applications to WASM, allowing to run them in the browser. There is even a Windows 2000 operating system (https://bellard.org/jslinux/vm.html?url=https://bellard.org/jslinux/win2k.cfg&mem=192&graphic=1&w=1024&h=768) compiled to WASM so you can play minesweeper as shown in Figure 1-4!

../images/469993_3_En_1_Chapter/469993_3_En_1_Fig4_HTML.png
Figure 1-4

Windows 2000 Running in the Browser

Which Browsers Support WebAssembly?

WebAssembly is supported by all major browsers: Chrome, Edge, Safari, Opera, and Firefox, including their mobile versions. You can verify support yourself by visiting https://caniuse.com/?search=WASM as shown in Figure 1-5.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig5_HTML.jpg
Figure 1-5

WebAssembly Support

As WebAssembly will become more and more important, we will see other browsers follow suit, but don’t expect Internet Explorer to support WASM.

WebAssembly and Mono

Mono is an open source implementation of the .NET CLI specification, meaning that Mono is a platform for running .NET assemblies. Mono is used in Xamarin (now called Multi-platform App UI, or MAUI for short) for building mobile applications that run on the Windows, Android, and iOS mobile operating systems. You can also use it to build applications for macOS, Linux, Tizen, and others. Mono also allows you to run .NET on Linux (its original purpose) and is written in C++. This last part is important because we saw that you can compile C++ to WebAssembly. So, what happened is that the Mono team decided to try to compile Mono to WebAssembly, which they did successfully. There are two approaches. One is where you take your .NET code and you compile it together with the Mono runtime into one big WASM application. However, this approach takes a lot of time because you need to take several steps to compile everything into WASM, not so practical for day-to-day development. The other approach takes the Mono runtime and compiles it into WASM, and this runs in the browser where it will execute .NET Intermediate Language just like normal .NET does. The big advantage is that you can simply run .NET assemblies without having to compile them first into WASM.

This is the approach currently taken by Blazor. In the beginning, Blazor used the Mono runtime, but they have now built their own .NET Core runtime for WebAssembly. But Blazor is not the only one taking this approach. For example, there is the Ooui project which allows you to run Xamarin.Forms applications in the browser. The disadvantage of this is that it needs to download a lot of .NET assemblies . This can be solved by using tree shaking algorithms which remove all unused code from assemblies. We will look at this in Chapter 15.

Interacting with the Browser with Blazor

WebAssembly with the .NET runtime allows you to run .NET code in the browser. Steve Sanderson used this to build Blazor. Blazor uses the popular ASP.NET MVC approach for building applications that run in the browser. MVC uses the razor syntax to generate HTML on the server. With Blazor, you build razor files (Blazor = Browser + Razor) which execute inside to browser to dynamically build a web page. With Blazor, you don’t need JavaScript to build a web app, which is good news for thousands of .NET developers who want to continue using C# (or F#). To use some browser features, you will still need JavaScript, and we will discuss this in Chapter 10.

How Does It Work?

Let’s start with a simple razor file in Listing 1-1 which you can find when you create a new Blazor project (which we will do further on in this chapter, no need to type anything yet).

Note

Each code sample has been formatted for readability, sometimes splitting lines where this is not necessary and using less indentation. I leave it to you how you decide to format your code.

@page "/counter"
<h1>Counter</h1>
<p role="status">Current count: @currentCount</p>
<button class="btn btn-primary"
        @onclick="IncrementCount">
  Click me
</button>
@code {
  private int currentCount = 0;
  private void IncrementCount()
  {
    currentCount++;
  }
}
Listing 1-1

The Counter Razor File

This file gets compiled into a .NET class (you’ll find out how later in this book) which is then executed by the Blazor engine. The result of this execution is a tree-like structure called the render tree . The render tree is then sent to JavaScript which updates the DOM to reflect the render tree (creating, updating, and removing HTML elements and attributes). Listing 1-1 will result in h1, p (with the contents set to the value of currentCount), and button HTML elements. When you interact with the page, for example, when you click the button, this will trigger the button’s click event which will invoke the IncrementCount method from Listing 1-1. The render tree is then regenerated, and any changes are sent again to JavaScript which will update the DOM. This process is illustrated in Figure 1-6.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig6_HTML.jpg
Figure 1-6

The Blazor WebAssembly DOM Generation Process

This model is very flexible. It allows you to build Progressive Web Apps, and your app can be embedded in Electron desktop applications of which Visual Studio Code is a prime example.

Blazor Server

At the August 7, 2018, ASP.NET community standup (www.youtube.com/watch?v=7Eh_l7jEcCo), Daniel Roth introduced a new execution model for Blazor now called Blazor Server. In this model, your Blazor site is running on the server resulting in a way smaller download for the browser.

We just saw that Blazor WebAssembly builds a render tree using the .NET runtime running in the browser which then gets sent to JavaScript to update the DOM. With Blazor Server, the render tree is built on the server using regular .NET and then gets serialized to the browser using SignalR (we will look at SignalR in a later chapter). JavaScript in the browser then deserializes the render tree to update the DOM. Pretty similar to the Blazor WebAssembly model. When you interact with the site, events get serialized back to the server which then executes the .NET code, updating the render tree, and the changes get serialized back to the browser. I’ve illustrated this process in Figure 1-7. The big difference is that there is no need to send the .NET runtime and your Blazor assemblies to the browser. And the programming model stays the same! You can switch Blazor Server-side and Blazor WebAssembly with just a couple of small changes to your code.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig7_HTML.jpg
Figure 1-7

Blazor Server Runtime Model

Pros and Cons of the Blazor Server

The Blazor Server model has a couple of benefits but also some drawbacks. Let’s discuss these here so you can decide which model fits your application’s needs.
  • Smaller downloads: With Blazor Server, your application does not need to download dotnet.wasm (the .NET runtime) nor all your .NET assemblies. The browser downloads a small JavaScript library which sets up the SignalR connection to the server. This means that the application will start a lot faster, especially on slower connections, but at the price that we continuously need a connection to the server to exchange small messages.

  • Development process: Blazor WebAssembly does not support all modern debugging capabilities, resulting in added logging. Because your .NET code is running on the server, you can use the regular .NET debugger with all of its advanced features. You could start building your Blazor application using the server-side model, and when it is finished, switch to the client-side model by switching the hosting model.

  • .NET APIs: Because you are running your .NET code on the server, you can use all the .NET APIs you would use with regular ASP.NET Core MVC applications, for example, accessing the database directly. Do note that doing this will stop you from quickly converting it into a client-side application. You can limit this by writing service classes and using dependency injection to inject different implementations depending on the environment your components are hosted in.

  • Online only: Running the Blazor application on the server does mean that your users will always need access to the server. This will prevent the application from running in Electron, nor will you be able to run it as a Progressive Web Application (PWA). And if the connection drops between the browser and server, your user could lose some work because the application will stop functioning. Blazor will try to reconnect to the server without losing any data, so most of the time, users will not lose any work done.

  • Server scalability: All your .NET code runs on the server, so if you have thousands of clients, your server(s) will have to handle all the work. Not only that, Blazor uses a stateful model which will require you to keep track of every user’s state on the server. So your server will need more resources than with Blazor WebAssembly which can use a stateless model.

Your First Blazor Project

Getting hands-on is the best way to learn. You will first install the prerequisites to developing with Blazor. Then you will create your first Blazor project, run the project to see it work, and finally inspect the different aspects of the project to get a “lay of the land” view for how Blazor applications are developed.

Note

I learned an important lesson from the first edition of this book: never underestimate the speed at which Microsoft innovates! All code samples in the first edition of Blazor Revealed became invalid quite rapidly. I do not expect this to happen again with this edition since it is based on the Release To Manufacture (RTM) version of Blazor. If something does not work, simply consult the sources that come with this book. I will keep these up to date. Promise!

The source code for this book is available on GitHub via the book’s product page, located at www.apress.com/ISBN.

Installing Blazor Prerequisites

Working with Blazor requires you to install some prerequisites, so in this section, you will install what is needed to get going.

Blazor runs on top of .NET , optionally providing the web server for your project which will serve the client files that run in the browser and run any server-side APIs that your Blazor project needs. .NET (previously known as .NET Core) is Microsoft’s cross-platform solution for working with .NET on Windows, Linux, and OSX.

You can find the installation files at www.microsoft.com/net/download. Look for the latest version of the .NET SDK (you’ll need at least version 6.0). Follow the installation instructions and install it on your machine, using Windows, OSX, or Linux.

Verify the installation when the installer is done by opening a new command prompt and typing the following command:
dotnet --version

Output should indicate that you installed the correct version. The version number should be at least 6.0.

Should the command’s output show an older version, you will need to download and install a more recent version of .NET SDK. These can run side by side so you will not break other .NET projects doing this.

Using Visual Studio

For people using Windows, Visual Studio (from now on, I will refer to Visual Studio as VS) is one of the integrated development environments (IDE) we will use throughout this book. If you are using OSX or Linux, you can use Visual Studio Code, and OSX users might prefer Visual Studio for Mac. With any one, you can edit your code, compile it, and run it all from the same application. And the code samples are also the same.

If you want to use Visual Studio, download the latest version of Visual Studio from www.visualstudio.com/downloads/. The Community Edition is free and should allow you to do everything done in this book.

Run the installer and make sure that you install the ASP.NET and web development role as shown in Figure 1-8.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig8_HTML.jpg
Figure 1-8

The Visual Studio Installer Workloads Selection

After installation, run Visual Studio from the Start menu. Then open the Help menu and select About Microsoft Visual Studio. The About Microsoft Visual Studio dialog window should specify at least version 17.0.0 as illustrated in Figure 1-9.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig9_HTML.jpg
Figure 1-9

About Microsoft Visual Studio

Using Visual Studio Code

Visual Studio Code (VSC) is a free, modern, cross-platform development environment with an integrated editor, git source control, and debugger. The environment has a huge range of extensions available allowing you to use all kinds of languages and tools directly from VSC. So, if you don’t have access to (because you’re running a non-Windows operating system or you don’t want to use) Visual Studio, use VSC.

Install VSC from www.visualstudio.com/. Install using the defaults.

After installation, you should install a couple of extensions for Code, especially the C# extension. Start Code, and at the left side, select the extensions tab as shown in Figure 1-10.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig10_HTML.jpg
Figure 1-10

Visual Studio Code Extensions Tab

You can search for extensions, so start with C# which is the first extension from Figure 1-11. This extension will give you IntelliSense and debugging for the C# programming language and .NET assemblies. You will probably get a newer version listed, so take the latest.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig11_HTML.jpg
Figure 1-11

C# for Visual Studio Code

Click Install.

Understanding the Blazor Templates for VS/Code

Throughout this book, we will create several different Blazor projects. With .NET Core, we can use the command-line interface (CLI) to create all kinds of projects, including Blazor WebAssembly and Blazor Server.

Let us begin by looking at the installed templates; you can list all installed templates using the following CLI command. You can execute this from a command prompt or from the VSC Terminal.
dotnet new --list
You will see four columns. The first shows the template’s description, the second column displays the name, the third lists the languages for which the template is available, and the last shows the tags, a kind of group name for the template. Among those listed are the following of interest:
Template Name                                 Short Name
--------------------------------------------  -------------------
Blazor Server App                             blazorserver
Blazor WebAssembly App                        blazorwasm
Class Library                                 classlib
Razor Class Library                           razorclasslib
Razor Component                               razorcomponent
xUnit Test Project                            xunit

With Blazor projects, you have a couple of choices. You can create a standalone Blazor project (using the blazorwasm template) that does not need server-side code. This kind of project known as Blazor WebAssembly has the advantage that you can simply deploy it to any web server which will function as a file server, allowing browsers to download your site just like any other site. We will look at deployment in a later chapter.

Or you can create a hosted project (adding the --hosted option) with client, server, and shared code. This kind of Blazor WebAssembly project will require you to host it where there is .NET Core support because you will execute code on the server as well, for example, to retrieve data from a database.

The third option is to run all Blazor code on the server (using the blazorserver template). In this case, the browser will use a SignalR connection to receive UI updates from the server and to send user interaction back to the server for processing.

In this book, we will use the second option (Blazor WebAssembly hosted on ASP.NET MVC Core) most of the time, but the concepts you will learn in this book are the same for all three options. You can even develop for Blazor WebAssembly and Blazor Server at the same time! Why? Because debugging support for Blazor WebAssembly is limited, so you develop with Blazor Server using all debugger features you know and love. But you can test everything with Blazor WebAssembly ensuring you can run everything in the browser later. This is the way I like to work. However, to pull this off, you need some experience with Blazor first, so keep reading.

Generating the Project with Dotnet CLI

To generate the project with dotnet CLI, which works on any machine, start by opening a command line, and change the current directory to wherever you want to create the project. Now execute the following command to create a new Blazor WebAssembly project. The dotnet is the command line, taking the new instruction, with the template being blazorwasm. The --hosted option will generate the server project as well. Finally, we tell it to generate everything in the MyFirstBlazor directory.
dotnet new blazorwasm --hosted -o MyFirstBlazor
This command will take a little while because it will download a bunch of NuGet packages from the Internet. When the command is ready, you can build your project using
cd MyFirstBlazor
dotnet build

This should build without any errors.

Now we can run the project from the command line using
cd MyFirstBlazor/Server
dotnet run
This will show you some output, including the URL of the Blazor application:
Building...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: https://localhost:5001
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5000
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: C:\Code\GitHub\Microsoft.Blazor.3rd\Ch01\MyFirstBlazor

Open your browser on this address (here https://localhost:5001), and you are ready to play!

Generating Your Project with Visual Studio

Start Visual Studio and select Create a new project.

Type Blazor in the search box, and select the Blazor WebAssembly App project template as illustrated in Figure 1-12.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig12_HTML.jpg
Figure 1-12

Visual Studio New Project Dialog

Click Next.

Name your project MyFirstBlazor, choose the location where the project should be generated, and click Next.

On the next screen, you can select the framework to use. Choose the latest version (at the time of writing, that is .NET 6.0), leave Authentication type set to None, check the ASP.NET Core hosted checkbox, and click Create. An example is shown in Figure 1-13.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig13_HTML.jpg
Figure 1-13

New ASP.NET Core Web Application

Wait for Visual Studio to complete. Then build and run your solution by pressing F5. After a little while, the browser will open and display the Blazor application.

Running Blazor with Visual Studio Code

After creating the project as we did with the CLI, open your solution’s folder (where the MyFirstBlazor.sln file sits) with VSC. You can do this from the command prompt
code .

Or you can open VSC and then select File ➤ Open Folder….

When Code has loaded everything (be patient), it will pop a question as in Figure 1-14. Answer Yes. This will add a folder called .vscode with configuration files adding support for building and running the project from Code. If you already have a .vscode folder (because you copied an existing project, for example), you will not get this question.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig14_HTML.jpg
Figure 1-14

Code Asking to Add Build and Debug Assets

Thanks to this integration with Visual Studio Code, you can simply press F5 to build and run your project.

Note

VSC now uses Workspace Trust which might pop up a dialog asking if you trust the authors of a project. When opening the provided code download, you will probably encounter this.

Running the Generated Project

Press F5 or Ctrl-F5 (no debugger) to run (this should work for both VS and VSC). Your (default) browser should open and display the home page as shown in Figure 1-15.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig15_HTML.jpg
Figure 1-15

Your First Application – Home Screen

This generated Single-Page Application (SPA) has on the left side a navigation menu allowing you to jump between different pages. On the right side, you will see the selected component; in Figure 1-15, it is showing the Index component. And in the top right corner, there is an About link to https://blazor.net/ which is the official Blazor documentation website.

The Index component shows the mandatory “Hello, world!” demo, and it also contains a survey component you can click to fill out a survey (this is a real survey, so please let Microsoft know you like Blazor!). The SurveyPrompt is the first example of a custom Blazor component. We will discuss building components like SurveyPrompt in Chapters 3 and 4.

In the navigation menu, click the Counter link. Doing so opens a simple screen with a number and a button as illustrated in Figure 1-16. Clicking the button will increment the counter. Try it!
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig16_HTML.jpg
Figure 1-16

Your First Application – Counter Screen

In the navigation menu, click the Fetch data link. Here, you can watch a (random and fake) weather forecast as shown in Figure 1-17. This forecast is generated on the server when asked by the client. This is very important because the client (which is running in the browser) cannot access data from a database directly, so many times, you need a server that can access databases and other data storage. Of course, if this was a Blazor Server application, you can access the database directly because you are running on the server.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig17_HTML.jpg
Figure 1-17

Your First Application – Fetch data Screen

Examining the Project’s Parts

Now being able to play with these pages is all nice, but let us have a look at how all this works. We will look starting with the server project which hosts our Blazor website. Then we will look at the shared project which contains classes used by both server and client. Finally, we will examine the client project which is the actual Blazor implementation.

Visual Studio, Visual Studio Code, and Visual Studio for Mac use solution files to group projects that will form an application. So, a typical Blazor WebAssembly project consists of a server, a client, and a shared project grouped into a single solution. This simplifies building everything since the solution allows tools to figure out in which order to compile everything. Hey, you could even switch between Visual Studio, VS for Mac, and VSC because they all use the same project and solution files!

The Server Project

Web applications are a bunch of files that get downloaded by the browser from a server. It is the server’s job to provide the files to the browser upon request. There is a whole range of existing servers to choose from, for example, IIS on Windows or Apache on Linux. ASP.NET Core has a built-in server known as Kestrel that you generated with the --hosted option, which you can then run on Windows, Linux, or OSX. This is the preferred option to use during development.

The topic of this book is Blazor, so we’re not going to discuss all the details of the server project that got generated (Microsoft has very good documentation on .NET Core at https://docs.microsoft.com/aspnet/core), but I do want to show you an important thing. In the server project (MyFirstBlazor.Server), look for Program.cs. Open this file and scroll down to the Configure section (look for the comment) shown in Listing 1-2.
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
  app.UseDeveloperExceptionPage();
  app.UseWebAssemblyDebugging();
}
else
{
  app.UseExceptionHandler("/Error");
  app.UseHsts();
}
app.UseHttpsRedirection();
app.UseBlazorFrameworkFiles();
app.UseStaticFiles();
app.UseRouting();
app.MapRazorPages();
app.MapControllers();
app.MapFallbackToFile("index.html");
Listing 1-2

The Server Project’s Program Class

The Configure section is responsible for installing middleware. Middleware objects are little .NET components that each have a clear responsibility. When you type in a URL, the browser sends an HTTP request to the server, which then passes it on to the middleware components in the listed order. Some of these will take the request and return a response, and some of them take the response and do something with it. Look at the first lines in Listing 1-3.
if (app.Environment.IsDevelopment())
{
  app.UseDeveloperExceptionPage();
  app.UseWebAssemblyDebugging();
}
else
{
  app.UseExceptionHandler("/Error");
  app.UseHsts();
}
Listing 1-3

The UseDeveloperExceptionPage Middleware

Would you like to see a detailed error page when the server has an uncaught exception? The UseDeveloperExceptionPage method which installs some error handling middleware takes care of that. Of course, you don’t need that in production (you should handle all exceptions correctly <grin>), so this middleware is only used when running in a development environment. How does the server know if you are running in development or release? The if statement you see here checks an environment variable called ASPNETCORE_ENVIRONMENT , and if the environment variable is set to Development, it knows you are running in development mode.

Open the launchSettings.json file in the server project’s Properties folder, as shown in Listing 1-4. Look at the MyFirstBlazor.Server profile. One of the settings in the profile sets this environment variable to Development which is the proper choice to use while writing your Blazor application.
{
  "iisSettings": {
    "windowsAuthentication": false,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:39361",
      "sslPort": 44358
    }
  },
  "profiles": {
    "MyFirstBlazor.Server": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
      "applicationUrl": "https://localhost:5001;http://localhost:5000",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    },
    "IIS Express": {
      "commandName": "IISExpress",
      "launchBrowser": true,
      "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}
Listing 1-4

The launchSettings.json File

The Blazor bootstrap process requires a bunch of special files, especially dotnet.wasm (dotnet.wasm is the .NET runtime compiled as WebAssembly). This is served by the Blazor middleware, which is installed by the UseBlazorFrameworkFiles instruction. Later in this chapter, you will see why.

Look at the end of Listing 1-2. Here is another important middleware installed. The MapFallbackToFile("index.html") will return the index.html file which takes care of loading everything your Blazor application needs.

Using a Shared Project

The FetchData component downloads weather information from the server. These kinds of requests will be handled by the MVC middleware (MapControllers). We will discuss this in more detail in Chapter 6.

The shape of the forecast data needs to be described in detail (computers are picky things), and in classic projects, you would describe this model’s shape twice, once for the client and again for the server because these would use different languages – C# on the server and JavaScript on the client. Not with Blazor! In Blazor, both client and server use C#, so we can describe the model once and share it between client and server as shown in Listing 1-5. As you can see, this is a simple C# class you could easily find in other kinds of projects.
namespace MyFirstBlazor.Shared;
public class WeatherForecast
{
  public DateTime Date { get; set; }
  public int TemperatureC { get; set; }
  public string? Summary { get; set; }
  public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
Listing 1-5

The Shared WeatherForecast Class

Understanding the Client Blazor Project

Open the client project’s wwwroot folder and look for index.html. The contents of that file should appear as shown in Listing 1-6. To be honest, this looks mostly like a normal HTML page. But on closer inspection, you’ll see that there is a div tag there with id app. This is where your Blazor application will go.
<div id="app">Loading...</div>

After this, there is another div; this is used to display errors in case your Blazor application has an uncaught exception.

../images/469993_3_En_1_Chapter/469993_3_En_1_Figa_HTML.gif

You will also find an <script> element near the end.
<script src="_framework/blazor.webassembly.js"></script>

This script will install Blazor by downloading dotnet.wasm. A little further we will look at this in more detail.

Listing 1-6. The index.html File

../images/469993_3_En_1_Chapter/469993_3_En_1_Figb_HTML.gif../images/469993_3_En_1_Chapter/469993_3_En_1_Figc_HTML.gif

Open Program.cs from the MyFirstBlazor.Client project as in Listing 1-7. Here, you see that the App component is associated with the app div from index.html. The #app string is a CSS selector which will find that div, and the Blazor runtime will replace it with the App component’s render tree.
builder.RootComponents.Add<App>("#app");
using Microsoft.AspNetCore.Components.WebAssembly.Hosting;
using MyFirstBlazor.Client;
var builder = WebAssemblyHostBuilder.CreateDefault(args);
builder.RootComponents.Add<App>("#app");
builder.Services.AddScoped(sp => new HttpClient { BaseAddress = new Uri(builder.HostEnvironment.BaseAddress) });
await builder.Build().RunAsync();
Listing 1-7

The Main Method

The main thing the App component does is to install the Router component as in Listing 1-8. You can find this code in the App.razor file in the client project. The router is responsible for loading a Blazor component depending on the URL in the browser. When the route is not found, it will display the <NotFound> content, which currently shows a simple not found message. For example, if you browse to the “/” URL, the router will look for a component with a matching @page directive.
<Router AppAssembly="@typeof(App).Assembly">
  <Found Context="routeData">
    <RouteView RouteData="@routeData"
               DefaultLayout="@typeof(MainLayout)" />
    <FocusOnNavigate RouteData="@routeData" Selector="h1" />
  </Found>
  <NotFound>
    <LayoutView Layout="@typeof(MainLayout)">
      <p role="alert">Sorry, there's nothing at this address.</p>
    </LayoutView>
  </NotFound>
</Router>
Listing 1-8

The App Component

In our current MyFirstBlazor project, this will match the Index component which you can find in the Index.razor file from the Pages folder. This component has the matching @page "/" directive so the router will display it for the / URL. This component displays a Hello World message and the survey link as in Listing 1-9.
@page "/"
<h1>Hello, world!</h1>
Welcome to your new app.
<SurveyPrompt Title="How is Blazor working for you?" />
Listing 1-9

The Index Component

Layout Components

Look at Figures 1-15, 1-16, and 1-17. All have the same menu. This menu is shared among all our Blazor components and is known as a layout component. We will discuss layout components in Chapter 9. But how does Blazor know which component is the layout component? Look again at Listing 1-8. When the route is found, it uses a default layout component called MainLayout. In our project, the layout component can be found in MainLayout.razor from the Shared folder, which I’ve listed in Listing 1-10.
@inherits LayoutComponentBase
<div class="page">
  <div class="sidebar">
    <NavMenu />
  </div>
  <main>
    <div class="top-row px-4">
      <a href="http://blazor.net" target="_blank" class="ml-md-auto">About</a>
    </div>
    <article class="content px-4">
       @Body
    </article>
  </main>
</div>
Listing 1-10

The MainLayout Component

This component contains a div HTML element with two nested divs. The first nested div with class sidebar contains a single Blazor component: NavMenu. This is where your navigation menu gets defined. The sidebar will display a menu, allowing you to navigate between Home, Counter, and Fetch data. We will look in more detail at navigation and routing in Chapter 9.

The next nested div with class main has two parts. The first is the About link you see on every page. The second part contains the @Body; this is where the selected page will be shown. For example, when you click the Counter link in the navigation menu, the @Body will be replaced with the Counter component.

This is all for now, but the rest of the book will explain each part as we go along.

Debugging Client-Side Blazor

Of course, while building your Blazor app, you will encounter unexpected behavior from time to time. Debugging Blazor Server can be done just like any .NET project using Visual Studio or Code. But with Blazor WebAssembly, your code will be running in the browser. You will be happy to learn that the VS/VSC debugger works with Blazor, although limited. You can put breakpoints in your code, step through your code, and observe variables holding simple types like bool, int, and string. At the time of writing, debugging Blazor WebAssembly only works for Chrome or Edge, both Chromium-based browsers.

Debugging with Visual Studio

To enable debugging with Visual Studio, open the launchSettings.json file from the project you will use as your startup project. With hosted Blazor WebAssembly, this is normally the server project. You will need to set the inspectUri property in here, like in Listing 1-11 (the template normally will configure this for you). This property enables the IDE to detect that this is a Blazor WebAssembly app and instructs the script debugging infrastructure to connect to the browser through the Blazor’s debugging proxy.
"MyFirstBlazor.Server": {
  "commandName": "Project",
  "dotnetRunMessages": true,
  "launchBrowser": true,
  "inspectUri": "{wsProtocol}://{url.hostname}:{url.port}/_framework/debug/ws-proxy?browser={browserInspectUri}",
  "applicationUrl": "https://localhost:5001;http://localhost:5000",
  "environmentVariables": {
    "ASPNETCORE_ENVIRONMENT": "Development"
  }
},
Listing 1-11

The launchSettings.json File for Debugging (Excerpt)

Now run your application in VS with the debugger by pressing F5. Be patient while your Blazor site starts to run (in Edge or Chrome!). Now you can put a breakpoint in your code, for example, on the IncrementCount method of the Counter component as in Figure 1-18, line 17. Simply click in the gray area left to your code (also known as the gutter) and a red dot will appear, indicating that the debugger will stop at this code.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig18_HTML.jpg
Figure 1-18

Setting a Breakpoint in the IncrementCount Method

Go back to your Blazor application and click the Counter’s Click Me button. The debugger should stop on the IncrementCount method. You can now examine the content of simple variables in the Locals window, like in Figure 1-19.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig19_HTML.jpg
Figure 1-19

Using the Locals Debugger Window to Inspect Simple Variables

Debugging with Visual Studio Code

Start VSC. Ensure you have the Microsoft.AspNetCore.Razor.VSCode.BlazorWasmDebuggingExtension debugging extensions installed as in Figure 1-20.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig20_HTML.jpg
Figure 1-20

The Blazor WASM Debugging Extension

You will also have to ensure that the JavaScript Preview Debugger has been enabled (by the time you are reading this, it might no longer be a preview feature). You can find this setting in VSC Settings like in Figure 1-21.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig21_HTML.jpg
Figure 1-21

Enable the JavaScript Preview Debugger

Open the folder containing the solution file. If it is the first time you open this folder with VSC, be patient, after a while, Figure 1-14 will pop up. Answer Yes. Also ensure Listing 1-11 is set up correctly like Visual Studio (this is actually independent of your IDE).

Now run your application in VSC with the debugger by pressing F5. Be patient while your Blazor site starts to run (in Chrome!). Now you can put a breakpoint in your code, for example, on the IncrementCount method of the Counter component as in Figure 1-22, line 16. Simply click in the area left to your code (also known as the gutter) and a red dot will appear, indicating that the debugger will stop at this code.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig22_HTML.jpg
Figure 1-22

Adding a Breakpoint in VSC

Go back to your Blazor application and click the Counter’s Click Me button. The debugger should stop on the IncrementCount method. You can now examine the content of simple variables in the Locals window, like in Figure 1-23.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig23_HTML.jpg
Figure 1-23

Inspecting Variables in VSC

Developing with Hot Reload

With .NET Core 6.0, Microsoft introduces a really nice feature called hot reload . This allows you to make changes to your code and markup while your application is running. As soon as you make the change, your application will update (hot reloads), even keeping the existing state of the application.

Hot Reload with .NET CLI

Let us start using hot reload using the command-line interface. Open a command prompt and change the directory to the MyFirstBlazor Server project and run
dotnet watch
This should start the server project (which is hosting the Blazor WebAssembly project), and the browser should also open with your application.
watch : Hot reload enabled. For a list of supported edits, see https://aka.ms/dotnet/hot-reload. Press "Ctrl + Shift + R" to restart.
Open the Counter component and increment the counter a couple of times. Now make a change to the Counter component, for example, Listing 1-12.
<h1>My First Counter</h1>
Listing 1-12

A Simple Change

As soon as you make the change, the browser will update itself, keeping the current count!

You can also change the code, for example, Listing 1-13.
private void IncrementCount()
{
  currentCount+=3;
}
Listing 1-13

Another Simple Change

Save. Clicking the Increment button will not add 3 to the counter.

If you want to restart again, go back to the command line and press Ctrl-Shift-R.

Hot Reload with Visual Studio

At the time of writing this chapter, hot reload does not work yet for Blazor WebAssembly application with Visual Studio. But, by the time you are reading this, it should work.

The Blazor WASM Bootstrap Process

At the bottom of Listing 1-14, you will find the <script> element responsible for bootstrapping Blazor in the browser. Let’s look at this process in detail.

Listing 1-14. The index.html File

../images/469993_3_En_1_Chapter/469993_3_En_1_Figd_HTML.gif../images/469993_3_En_1_Chapter/469993_3_En_1_Fige_HTML.gif

Run the Blazor application. Open the browser’s developer tools (most browsers will open the developer tools when you press F12). We will have a look at what happens at the network layer.

Note

In all screenshots, I will be using the Edge browser which is very similar to Chrome. If you prefer to use another browser, go right ahead since all modern desktop browsers have debugging support.

First, open the browser debugger’s Application tab, and press the Clear site data button as in Figure 1-24. This will clear the browser’s cache and will give you a better view what happens when someone visits a Blazor WebAssembly application for the first time.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig24_HTML.jpg
Figure 1-24

Clearing the Browser’s Storage

Now open the browser debugger’s Network tab. Refresh your browser (empty cache and hard refresh) to see what gets downloaded from the server as in Figure 1-25. First, you will see index.html (shown as localhost) being downloaded, which in turn downloads bootstrap.min.css and app.css, and then blazor.webassembly.js. A little lower, you will see that blazor.boot.js gets downloaded, which in turn will download dotnet.wasm. This is the .NET Core runtime compiled to run on WebAssembly!
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig25_HTML.jpg
Figure 1-25

Examining the Bootstrap Process Using the Network Log

Now that the .NET runtime is running, you will see (scroll down?) that MyFirstBlazor.Client.dll gets downloaded, followed by all its dependencies, including mscorlib.dll and system.dll. These files contain the .NET libraries containing classes such as string used to execute all kinds of things, and they are the same libraries you use on the server. This is very powerful because you can reuse existing .NET libraries in Blazor you or others built before!

At the bottom of the Network tab, you will see the total download size as in Figure 1-26. Almost 10 MB! This is because we are using an empty cache; the next download will show a lot less as shown in Figure 1-27 because now Blazor can retrieve most of the files from the cache. We will look at reducing the full download size for production applications in Chapter 15.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig26_HTML.jpg
Figure 1-26

Total Download Size with Empty Cache

../images/469993_3_En_1_Chapter/469993_3_En_1_Fig27_HTML.jpg
Figure 1-27

Total Download Size with Filled Cache

Let us now compare this with Blazor Server.

The Blazor Server Bootstrap Process

Let’s look at the bootstrapping process of a Blazor Server project.

Open a command line and run the following command which will create a new Blazor Server project and solution:
dotnet new blazorserver -o BlazorServerBootstrap
Now we can build and run this application:
cd .\BlazorServerBootstrap\
dotnet run
Should you get an error like the following line, it means another project is still running. Stop that project first and retry.
Failed to bind to address https://127.0.0.1:5001: address already in use
Open the browser, and go to https://localhost:5001. The Blazor application should be shown. Now open the browser’s debugger on the Network tab, disable the cache (on the Network tab, you have a checkbox to disable the cache), and make your page refresh. Now compare what gets downloaded as in Figure 1-28. As you can see, the total download size is a lot smaller, resulting in your page getting loaded faster. You can also see that a WebSocket is opened between server and browser, allowing the Blazor runtime to exchange UI changes and events.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig28_HTML.jpg
Figure 1-28

Looking at Server-Side Blazor Network Activity

Now click the Counter link in the navigation menu and select the websocket link in the network debugger tab. Each time you click, you will see a couple of SignalR messages appear as in Figure 1-29. These binary messages are all tiny because only changes are transmitted this way. For example, when you click the Increment button in the Counter component, the browser only needs to update the number in the browser.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig29_HTML.png
Figure 1-29

The SignalR Messages

Nullable Reference Types

Throughout this book, I will be using modern C# with some of the latest features. But there is one C# feature I want to discuss right now. Every developer, from time to time, will encounter a NullReferenceException, which is a real bug because you can always avoid it. What if the compiler can help you with this and warn you about a possible NullReferenceException? This is what the section “Nullable Reference Types” is all about.

An Apology

Who invented the null pointer? Tony Hoare did, and he apologized in 2009 and denoted this as his billion-dollar mistake (www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/):

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

Many object-oriented programming languages still use the null pointer , and C# is no exception. Some languages even treated null differently. For example, in Objective-C, when a pointer is null, the compiler would not invoke a method on it. And it would do this silently! Of course, you would not get a NullReferenceException, but it did skip an important piece of functionality.

Using Null in C#

Let us start with the basics. In .NET, there are two different kinds of types: reference types and value types. A reference type uses a reference to point to an object, and a value type holds the value of an object. Because of this, value types cannot be null. But in databases, you can have a column holding a number (a value type) which can be nil. So how do you represent this in C#? For this, we can denote a nullable value type by adding a question mark after the type, for example, in Listing 1-15.
int? i = null;
Listing 1-15

A Nullable Value Type

Now in C#, we can tell the compiler to treat reference types in the same way, meaning that we will get a warning if we assign a null value to a reference type, except when we add a question mark after the type. Listing 1-16 shows both examples.
// No warning
string? canBeNull = null;
// Warning:
// Converting null literal or possible null value to non-nullable type
string cannotBeNull = null;
Listing 1-16

Nullable Reference Types

Of course, this would break every existing C# application out there, so we need to enable this in our project properties. You can do this in Visual Studio using your project properties as shown in Figure 1-30.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig30_HTML.jpg
Figure 1-30

Setting the Nullable Compiler Option

You can also do this directly in your project as shown in Listing 1-17.
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net5.0</TargetFramework>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>
Listing 1-17

Enabling Nullable Reference Types in the Project File

This causes the compiler to set a nullable flag for every field, property, and method that is of (or returns) a reference type. You can inspect this flag in VS by hovering over it as shown in Figure 1-31.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig31_HTML.jpg
Figure 1-31

Inspecting the Nullable Flag

The compiler then uses that flag to issue warnings if you would attempt to use a nullable reference type, for example, by getting the length of the string. Figure 1-32 will display a warning because the canBeNull reference can be null.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig32_HTML.jpg
Figure 1-32

Possible Null Reference

However, if we nest this in a condition as in Figure 1-33 where we check against null, the compiler will no longer issue a warning.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig33_HTML.jpg
Figure 1-33

No Possible Null Reference

So the whole idea of nullable reference types is to make the compiler do the analysis and to issue a warning when we can have a possible null being used which would result in a NullReferenceException.

Using References

In C#, we can declare a class with an example in Listing 1-18. But when you do this with nullable reference types enabled, you will get compiler warnings. Why? Because you can create a new instance of a Person with a null FirstName and/or a null LastName. So again, the compiler will warn about this.
public class Person
{
  public string FirstName { get; set; }
  public string LastName { get; set; }
}
Listing 1-18

A Person Class

There are a couple of ways we can make the compiler stop issuing warnings. We can use a constructor as in Listing 1-19. Now we cannot create a Person instance with a null property. Should someone call this constructor with a null argument, the compiler will again issue a warning.
public class Person
{
  public Person(string firstName, string lastName)
  {
    FirstName = firstName;
    LastName = lastName;
  }
  public string FirstName { get; set; }
  public string LastName { get; set; }
}
Listing 1-19

Using a Constructor

Sometimes you simply cannot use a constructor to silence the compiler. For example, you might want to use this Person class with Entity Framework Core. In this case, you could make FirstName and LastName nullable as in Listing 1-20.
public class Person
{
  public string? FirstName { get; set; }
  public string? LastName { get; set; }
}
Listing 1-20

Person with Nullable Name

However, this does not mimic real life. There is another technique we can use.

The Null-Forgiving Operator

Sometimes you just know that a nullable reference is not null, and you want to tell the compiler about this. For this, we can use the null-forgiving operator by appending the nullable reference with an exclamation mark as in Figure 1-34. This sets the nullable flag to false, and the compiler is happy.
../images/469993_3_En_1_Chapter/469993_3_En_1_Fig34_HTML.jpg
Figure 1-34

The Null-Forgiving Operator

We can even use this to have a null value with the nullable flag set to false! What?! Let us look at the Person class again. When we want to use this class with a library such as Entity Framework Core and we trust this library to always provide us with non-null values, we can silence the compiler as in Listing 1-21. This looks weird. Here, we assign the null! value, whose nullable flag is set to false so the compiler does not give us warnings.
public class Person
{
  public string? FirstName { get; set; } = null!;
  public string? LastName { get; set; } = null!;
}
Listing 1-21

Using the Null-Forgiving Operator with Types

This is exactly the technique we will use to create Blazor components that have reference properties that we cannot initialize using a constructor.

Of course, with string properties, we can also assign them an empty string instead of null as in Listing 1-22. But for other reference types, this is not always possible.
public class Person
{
  public string? FirstName { get; set; } = string.Empty;
  public string? LastName { get; set; } = string.Empty;
}
Listing 1-22

The Person Class with Empty Name.

Nullable Reference Types and .NET Libraries

Microsoft has gone through a lot of effort to make all their libraries support nullable reference types. I want you to realize that this is all compiler meta-data, so you can use the new libraries supporting nullable reference types with older projects; the compiler will simply ignore this meta-data. You can also use libraries that do not support this meta-data, but you will need to use the null-forgiving operator with a lot of methods. But do yourself a favor – get to use nullable reference types and your code will be shipped with a lot less bugs! You can learn more at https://docs.microsoft.com/en-us/dotnet/csharp/nullable-references.

Summary

In this chapter, we looked at the history of the browser wars and how this resulted in the creation of WebAssembly. The .NET runtime allows you to run .NET assemblies, and because it can now also run on WebAssembly, we can now run .NET assemblies in the browser! All of this resulted in the creation of Blazor, where you build razor files containing .NET code which update the browser’s DOM, giving us the ability to build Single-Page Applications in .NET, instead of JavaScript.

First, we installed the prerequisites needed for developing and running Blazor applications. We then created our first Blazor project. This project will be used throughout this book to explain all the Blazor concepts you need to know about. We examined this solution, looking at the server-side project, the shared project, and the Blazor project, and compared the bootstrap process for both Blazor WebAssembly and Blazor Server.

Finally, we looked at using nullable reference types and how this can help writing better code with less bugs.