<?xml version="1.0" encoding="utf-8"?><rss xmlns:a10="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Kristoffer Strube’s Blog</title><link>https://kristoffer-strube.dk/</link><description>On this blog I will post about my hobby projects involving .NET, Blazor, browser specifications, and much more!</description><language>en</language><managingEditor>blog@kristoffer-strube.dk</managingEditor><lastBuildDate>Tue, 16 Jul 2024 23:09:46 +0200</lastBuildDate><ttl>120</ttl><a10:id>https://kristoffer-strube.dk</a10:id><item><guid isPermaLink="false">multithreading-in-blazor-wasm-using-web-workers</guid><link>https://kristoffer-strube.dk/post/multithreading-in-blazor-wasm-using-web-workers/</link><title>Multithreading in Blazor WASM using Web Workers</title><description>Blazor has multiple rendering models, each with its pros and cons. One of the most popular is Blazor WASM, which enables a highly interactive web application running entirely in the client's browser. But its forte is also its weakness; when you offload the work of running the application to the browser, you must live under its constraints. One of these constraints is that each window in a browser is inherently single-threaded. A way to still do work on multiple threads is by using Web Workers, which are analog to Threads in .NET. In this article, we will go through how to use two of the most prominent Blazor Web Workers OSS libraries; we will show a separate Web Workers implementation that I have created myself which uses the experimental-wasm .NET workload and, in the end, do a little comparison of how each of them performs for a variety of workloads.</description><pubDate>Wed, 17 Jul 2024 00:00:00 +0200</pubDate><a10:updated>2024-07-17T00:00:00+02:00</a10:updated><a10:content type="text">&lt;h3&gt;Web Workers&lt;/h3&gt;
&lt;p&gt;Web Workers are defined as a part of &lt;a href="https://html.spec.whatwg.org/multipage/workers.html#workers"&gt;the HTML specification&lt;/a&gt;. They enable us to run some scripts in the background to avoid blocking the primary thread when we do heavy work. This can be a common problem in Blazor WASM, where the UI can become unresponsive when a heavy workload is executed.&lt;/p&gt;
&lt;p&gt;In JavaScript, we can create a worker and listen for messages posted in the worker like so:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;let worker = new Worker(&amp;quot;my-worker-script.js&amp;quot;, { type: &amp;quot;classic&amp;quot; });

worker.addEventListener(&amp;quot;message&amp;quot;, log)

function log(e) {
    console.log(&amp;quot;Some message from the worker: &amp;quot; + e.data);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The content of the &lt;code&gt;my-worker-script.js&lt;/code&gt; file would then define the worker's work. An example could be posting a message to itself, which is what we listen for in the previous code block.&lt;/p&gt;
&lt;h4&gt;my-worker-script.js&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;self.postMessage(&amp;quot;This was posted from the worker!&amp;quot;);

console.log(&amp;quot;We can also log to the console directly from here.&amp;quot;)
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Tewr.BlazorWorker&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/Tewr/BlazorWorker"&gt;Tewr.BlazorWorker&lt;/a&gt; is one of the first Web Workers abstractions made for Blazor WASM. It is made by &lt;a href="https://github.com/Tewr"&gt;Tewr&lt;/a&gt;, whom you might also know from the &lt;a href="https://github.com/Tewr/BlazorFileReader"&gt;BlazorFileReader&lt;/a&gt; library. It uses the &lt;a href="https://github.com/esskar/Serialize.Linq"&gt;Serialize.Linq&lt;/a&gt; library to serialize expressions that represent the work that is to be done on the background thread. First, the worker is initialized, which also starts the Blazor runtime from the worker thread. Then, the serialized expression is posted to the worker. The Blazor application, which the worker initializes, listens for messages sent to the worker, and when it sees a new message, it deserializes the expressions and invokes it.&lt;/p&gt;
&lt;p&gt;The following is a minimal sample of what you need to do some simple work on another thread.
First, you need to install this NuGet package&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;Tewr.BlazorWorker.BackgroundService
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then add its service to your service collection in &lt;code&gt;Program.cs&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;builder.Services.AddWorkerFactory();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, on a page, make a setup like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@using BlazorWorker.BackgroundServiceFactory
@using BlazorWorker.Core
@inject IWorkerFactory workerFactory

&amp;lt;button @onclick=ExecuteOnTewrBlazorWorker&amp;gt;Execute&amp;lt;/button&amp;gt;
&amp;lt;br/&amp;gt;
&amp;lt;code&amp;gt;@result&amp;lt;/code&amp;gt;

@code {
    private string result = &amp;quot;&amp;quot;;

    private async Task ExecuteOnTewrBlazorWorker()
    {
        IWorker worker = await workerFactory.CreateAsync();
        var service = await worker.CreateBackgroundServiceAsync&amp;lt;MathService&amp;gt;();

        double input = Random.Shared.NextDouble();

        double output = await service.RunAsync(math =&amp;gt; math.MutliplyByTwo(input));

        result = $&amp;quot;{input} * 2 = {output}&amp;quot;;
    }

    public class MathService
    {
        public double MutliplyByTwo(double input)
        {
            return input * 2;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above code sample initializes a service called &lt;code&gt;MathService&lt;/code&gt; in the worker and then invokes the method &lt;code&gt;MutliplyByTwo&lt;/code&gt; with a random number created on the main thread. The sample is very basic, and obviously, multiplying a number by two is not very expensive. But the idea is still recognizable: Taking some input from the main thread, running some method on the worker thread, and, in the end, returning the result to the main thread.&lt;/p&gt;
&lt;p&gt;After going through this minimal sample, my first impression is that it indeed feels pretty minimal. The only part I'm unsure about is the use of &lt;code&gt;Serialize.Linq&lt;/code&gt;, which it uses to deserialize and compile the expression every time it is called. Depending on how well the .NET WASM runtime can optimize the generated IL code, this could be expensive.&lt;/p&gt;
&lt;h3&gt;SpawnDev.BlazorJS.WebWorkers&lt;/h3&gt;
&lt;p&gt;&lt;a href="https://github.com/LostBeard/SpawnDev.BlazorJS?tab=readme-ov-file#spawndevblazorjswebworkers"&gt;SpawnDev.BlazorJS.WebWorkers&lt;/a&gt; is a newer implementation which is a part of &lt;a href="https://github.com/LostBeard"&gt;LostBeard&lt;/a&gt;s project &lt;a href="https://github.com/LostBeard/SpawnDev.BlazorJS"&gt;SpawnDev.BlazorJS&lt;/a&gt;. It likewise uses &lt;a href="https://github.com/esskar/Serialize.Linq"&gt;Serialize.Linq&lt;/a&gt; to serialize the expression that should be evaluated on the worker. But it requires a bit more setup.&lt;/p&gt;
&lt;p&gt;Let's see an equivalent to the previous sample but with &lt;code&gt;SpawnDev.BlazorJS.WebWorkers&lt;/code&gt;. We first need to add their NuGet package:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;SpawnDev.BlazorJS.WebWorkers
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we need to do a bit of setup in &lt;code&gt;Program.cs&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;builder.Services.AddBlazorJSRuntime();
builder.Services.AddWebWorkerService();
builder.Services.AddSingleton&amp;lt;IMathService, MathService&amp;gt;();

await builder.Build().BlazorJSRunAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We add the services needed for &lt;code&gt;BlazorJS&lt;/code&gt; to work, a service needed for accessing and creating web workers, and inject &lt;code&gt;MathService&lt;/code&gt; as a service. Then, we change a significant part of our &lt;code&gt;Program.cs&lt;/code&gt; file. We normally call &lt;code&gt;RunAsync()&lt;/code&gt; after building the &lt;code&gt;WebAssemblyHost&lt;/code&gt; but here we call &lt;code&gt;BlazorJSRunAsync()&lt;/code&gt; instead. This is the primary part that changes the control flow of our Blazor application. The &lt;code&gt;BlazorJSRunAsync&lt;/code&gt; method checks whether we are running in the window context, in which case it starts Blazor like usual. Otherwise, it simply idles and listens for messages.&lt;/p&gt;
&lt;p&gt;We have updated our &lt;code&gt;MathService&lt;/code&gt; a bit by making it implement an interface. This is necessary so the library can create a proxy for the service.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public interface IMathService
{
    public double MutliplyByTwo(double input);
}

public class MathService : IMathService
{
    public double MutliplyByTwo(double input)
    {
        return input * 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, on some page, we can make our minimal sample again:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@using SpawnDev.BlazorJS.WebWorkers
@inject WebWorkerService WebWorkerService

&amp;lt;button @onclick=ExecuteOnSpawnDevWebWorker&amp;gt;Execute&amp;lt;/button&amp;gt;
&amp;lt;br/&amp;gt;
&amp;lt;code&amp;gt;@result&amp;lt;/code&amp;gt;

@code {
    private string result = &amp;quot;&amp;quot;;

    private async Task ExecuteOnSpawnDevWebWorker()
    {
        WebWorker? worker = await WebWorkerService.GetWebWorker();

        double input = Random.Shared.NextDouble();

        double output = await worker!.Run&amp;lt;IMathService, double&amp;gt;(job =&amp;gt; job.MutliplyByTwo(input));

        result = $&amp;quot;{input} * 2 = {output}&amp;quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This was a lot more setup than the &lt;code&gt;Tewr&lt;/code&gt; sample, even though it seems to use the same core principles. However, that can also be helpful, as it makes certain parts of the internals more transparent to us. This can help library users troubleshoot problems with greater ease.&lt;/p&gt;
&lt;h3&gt;KristofferStrube.Blazor.WebWorkers&lt;/h3&gt;
&lt;p&gt;Before looking at how the other solutions worked, I tried to implement my own wrapper for calling .NET through Web Workers. It differs by two key points.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;It does not start a separate Blazor instance in the worker but instead starts a .NET application using the &lt;code&gt;wasm-experimental&lt;/code&gt; workload.&lt;/li&gt;
&lt;li&gt;It does not use &lt;code&gt;Serialize.Linq&lt;/code&gt; and instead enforces a standard format for a job that needs to be implemented in a separate project.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;This makes the implementation a bit more limited. The work being executed by the worker must have a single input and output. Apart from this, you can't use normal Blazor JSInterop in a &lt;code&gt;wasm-experimental&lt;/code&gt; project. But if we assume you don't need to use JSInterop for your background job, then this should be fine. So, let's see what it looks like.&lt;/p&gt;
&lt;p&gt;You first need to install the &lt;code&gt;wasm-experimental&lt;/code&gt; workload&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet workload install wasm-experimental
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, you need to create a separate project in your solution. You can create this project using the standard .NET 8 console template&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet new console
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To make this a &lt;code&gt;wasm-experimental&lt;/code&gt; project, you must adjust the &lt;code&gt;.csproj&lt;/code&gt; file to look like this.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;Project Sdk=&amp;quot;Microsoft.NET.Sdk&amp;quot;&amp;gt;

  &amp;lt;PropertyGroup&amp;gt;
    &amp;lt;TargetFramework&amp;gt;net8.0&amp;lt;/TargetFramework&amp;gt;
    &amp;lt;OutputType&amp;gt;Exe&amp;lt;/OutputType&amp;gt;
    &amp;lt;RuntimeIdentifier&amp;gt;browser-wasm&amp;lt;/RuntimeIdentifier&amp;gt;
    &amp;lt;Nullable&amp;gt;enable&amp;lt;/Nullable&amp;gt;
    &amp;lt;ImplicitUsings&amp;gt;enable&amp;lt;/ImplicitUsings&amp;gt;
    &amp;lt;AllowUnsafeBlocks&amp;gt;true&amp;lt;/AllowUnsafeBlocks&amp;gt;
  &amp;lt;/PropertyGroup&amp;gt;

  &amp;lt;ItemGroup&amp;gt;
    &amp;lt;PackageReference Include=&amp;quot;KristofferStrube.Blazor.WebWorkers&amp;quot; Version=&amp;quot;0.1.0-alpha.6&amp;quot; /&amp;gt;
  &amp;lt;/ItemGroup&amp;gt;

&amp;lt;/Project&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we need to add a class that implements the abstract &lt;code&gt;JsonJob&lt;/code&gt; class to the &lt;code&gt;wasm-experimental&lt;/code&gt; project. The &lt;code&gt;JsonJob&lt;/code&gt; class has a single abstract method called &lt;code&gt;Work&lt;/code&gt;, where you should implement the work that the job needs to do. The two type parameters of the &lt;code&gt;JsonJob&lt;/code&gt; class define the input and output of the job.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using KristofferStrube.Blazor.WebWorkers;

public class MultiplyByTwoJob : JsonJob&amp;lt;double, double&amp;gt;
{
    public override double Work(double input)
    {
        return input * 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;To finish the worker part, we just need to update our &lt;code&gt;Program.cs&lt;/code&gt; to start our job. Before this, we also checked that it was running in the browser, as this depends on using the interop services for JavaScript, which are only available when running in the browser.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;if (!OperatingSystem.IsBrowser())
    throw new PlatformNotSupportedException(&amp;quot;Can only be run in the browser!&amp;quot;);

await new MultiplyByTwoJob().StartAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We then need to take a dependency on the &lt;code&gt;wasm-experimental&lt;/code&gt; project from our main Blazor project, and then we are ready to make our minimal sample&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@using KristofferStrube.Blazor.WebWorkers
@inject IJSRuntime JSRuntime

&amp;lt;button @onclick=ExecuteOnBlazorWebWorkers&amp;gt;Execute&amp;lt;/button&amp;gt;
&amp;lt;br/&amp;gt;
&amp;lt;code&amp;gt;@result&amp;lt;/code&amp;gt;

@code {
    private string result = &amp;quot;&amp;quot;;

    private async Task ExecuteOnBlazorWebWorkers()
    {
        var worker = await JobWorker&amp;lt;double, double, MultiplyByTwoJob&amp;gt;.CreateAsync(JSRuntime);

        double input = Random.Shared.NextDouble();

        double output = await worker.ExecuteAsync(input);

        result = $&amp;quot;{input} * 2 = {output}&amp;quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;My solution required even more setup to get the minimal sample up and running, but it also differs a lot, so there might be other gains from this.&lt;/p&gt;
&lt;h3&gt;Performance comparisons&lt;/h3&gt;
&lt;p&gt;Now that we have seen three ways to do the same work in the background, let's make some simple benchmarks of how the different solutions perform in various settings. In all these experiments, we will repeat the experiment 1000 times and then take the average of the best 100 results to avoid including outliers. We will also compare the three wrapper implementations with the same work implemented in JavaScript.&lt;/p&gt;
&lt;p&gt;All of the following comparisons are also available here if you want to check the code directly or try to reproduce the results on your own machine:&lt;/p&gt;
&lt;p&gt;Repository: &lt;a href="https://github.com/KristofferStrube/Blazor.WorkersBenchmarks"&gt;https://github.com/KristofferStrube/Blazor.WorkersBenchmarks&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;Live Demo: &lt;a href="https://kristofferstrube.github.io/Blazor.WorkersBenchmarks/"&gt;https://kristofferstrube.github.io/Blazor.WorkersBenchmarks/&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Sending large inputs&lt;/h4&gt;
&lt;p&gt;It can be expensive to send the parameters needed to run the job. This can be one of the reasons why it might not be worth it to use a worker, as the additional workload of serializing the input would still need to be done on the main thread.&lt;/p&gt;
&lt;p&gt;To compare this, we have constructed a simple job that receives a string and always returns the number 42. We intentionally do not use the input to calculate something, as we would then measure the processing time of the input as well.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class LargeInputJob : JsonJob&amp;lt;string, int&amp;gt;
{
    public override int Work(string input)
    {
        return 42;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's see the result of this small benchmark.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/large-input.png" alt="Results from measuring the average running time for different worker wrappers" /&gt;
Surprisingly, &lt;code&gt;Tewr.Blazor.Worker&lt;/code&gt; seems to be much slower than &lt;code&gt;SpawnDev.BlazorJS.WebWorkers&lt;/code&gt;. It appears to have a larger overhead from starting some work, but it also grows in running time much faster than the other implementations. If we create a linear fit for the four different sets of measurements, we get the following base overheads and growth rates.&lt;/p&gt;
&lt;br /&gt;
&lt;table&gt;
&lt;tr&gt;&lt;th&gt;Worker Implementation&lt;/th&gt;&lt;th&gt;Base in milliseconds&lt;/th&gt;&lt;th&gt;Growth in milliseconds per 10000 chars&lt;/th&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Tewr.BlazorWorker&lt;/td&gt;&lt;td&gt;5.776&lt;/td&gt;&lt;td&gt;1.033&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;SpawnDev.BlazorJS.WebWorkers&lt;/td&gt;&lt;td&gt;1.576&lt;/td&gt;&lt;td&gt;0.196&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;KristofferStrube.Blazor.WebWorkers&lt;/td&gt;&lt;td&gt;0.813&lt;/td&gt;&lt;td&gt;0.150&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;JS Worker&lt;/td&gt;&lt;td&gt;0.317&lt;/td&gt;&lt;td&gt;0.087&lt;/td&gt;&lt;/tr&gt;
&lt;table&gt;
&lt;br /&gt;
&lt;p&gt;I like to present these numbers as they make clear what can be challenging to read from the plot. The primary interesting part that we can read from these numbers is that the different sets of measurements will get the same order if we sort them by their base overhead or if we sort them by their growth rate. This means that using JS for this type of work will always be most favorable, no matter how big our input is. But if we were to limit this comparison to only include .NET implementations, then our solution would be the best. That JS won is unsurprising as we need to send our string through JSInterop two times when working with the .NET implementations. First, send it from .NET to JS, then send it back to our Blazor or &lt;code&gt;wasm-experimental&lt;/code&gt; application from JS in the worker.&lt;/p&gt;
&lt;h4&gt;CPU-intensive work&lt;/h4&gt;
&lt;p&gt;Our next benchmark is going to measure the speed of the work being done on the worker itself. This is an especially interesting experiment as it will compare JavaScript against Compiled Expressions and .NET pre-compiled. For this reason, we will also measure both AOT and non-AOT in this benchmark to see how big this influence has on the different .NET implementations. The work we are going to measure in this sample is going to be very simple but, nonetheless, very CPU intensive when run for a big input. We will run a for loop up to the input number and sum up all the even numbers. This is the kind of work that could freeze the UI in Blazor if we were to run it on the main thread. This time, we will return the result, not because we wish to measure how long larger numbers take to return but for reasons that will become clear soon. This job is implemented like this in .NET:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class SumEvenNumbersJob : JsonJob&amp;lt;int, int&amp;gt;
{
    public override int Work(int input)
    {
        int sum = 0;
        for (int i = 0; i &amp;lt; input; i++)
        {
            if (i % 2 == 0)
            {
                sum += i;
            }
        }
        return sum;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's see the results.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/sum-even-numbers.png" alt="Results from measuring the average running time for summing even numbers up to some input with different worker wrappers" /&gt;&lt;/p&gt;
&lt;p&gt;We first see that we have the same order as before when sorting by speed. But this time, we don't see a clear difference in which .NET implementation grows the fastest. We still see that the JS worker is the fastest and it seems to grow much slower than the other implementations.&lt;/p&gt;
&lt;p&gt;But all is not lost. Let's repeat this benchmark with the project AOT compiled. This was why we ensured that the sum was returned in this sample, as AOT would else remove the for-loop entirely if the sum had not been used. This should make a considerable improvement for our .NET implementations, as we have seen in our last article on this topic: &lt;a href="https://kristoffer-strube.dk/post/a-holistic-comparison-of-blazor-wasm-performance-from-aspnet-core-5-to-8/"&gt;A holistic comparison of Blazor WASM performance from ASP.NET Core 5 to 8&lt;/a&gt;. We will start this experiment with some bigger inputs as the .NET implementations are now so efficient that they would seem not to change at the previous scale.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/sum-even-numbers-aot.png" alt="Results from measuring the average running time for summing even numbers up to some input with different worker wrappers that are AOT compiled" /&gt;&lt;/p&gt;
&lt;p&gt;Notice that we now increment our input with 1 million instead of 10 thousand for each of the points at which we measure time. This is why the JS workers' change in speed seems steeper now, even though the speed has not changed significantly. But let's focus on what we are interested in. .NET is faster than JS for the same job on a worker thread! This is the first time I have seen a direct comparison of .NET against JavaScript, so I was excited to know that .NET can outperform JS for CPU-intensive work in the browser.&lt;/p&gt;
&lt;h4&gt;Reading, processing, and outputting&lt;/h4&gt;
&lt;p&gt;The next case is a more complete demonstration of a real workload. We will make a mini-version of the &lt;a href="https://github.com/gunnarmorling/1brc"&gt;The One Billion Row Challenge&lt;/a&gt;. The challenge at its core is to read some files with temperature measurements and then output each city's minimum, average, and maximum temperatures. To make this a bit simpler, I have chosen to create an endpoint on my personal API that can return measurements for some number of cities. Apart from this, the task is still the same. This also makes it closer to some standard browser work: getting some string, parsing it, reading through it, and reporting aggregated results. I have tried to find the simplest possible solution, as I could not make the same performance improvements I know of in .NET when making the JS implementation.&lt;/p&gt;
&lt;p&gt;This is the simple implementation in .NET:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;using KristofferStrube.Blazor.WebWorkers;

public class AverageCityTemperaturesJob(HttpClient httpClient) : TaskJsonJob&amp;lt;int, CityStatistics[]&amp;gt;
{
    private const string MeasurementsEndpoint = &amp;quot;https://kristoffer-strube.dk/API/not-the-measurment-endpoint/&amp;quot;;
    private string? responseAsText;

    public override async Task&amp;lt;CityStatistics[]&amp;gt; Work(int input)
    {
        if (responseAsText is null)
        {
            responseAsText = await httpClient.GetStringAsync(MeasurementsEndpoint + input);
        }

        Dictionary&amp;lt;string, CityAggregate&amp;gt; aggregates = new();

        foreach (string line in responseAsText.Split(&amp;quot;\n&amp;quot;))
        {
            string[] parts = line.Split(&amp;quot;;&amp;quot;);
            string city = parts[0];
            float temperature = float.Parse(parts[1]);

            if (!aggregates.TryGetValue(city, out CityAggregate? aggregate))
            {
                aggregate = new();
                aggregates[city] = aggregate;
            }
            aggregate.Min = temperature &amp;lt; aggregate.Min ? temperature : aggregate.Min;
            aggregate.Max = temperature &amp;gt; aggregate.Max ? temperature : aggregate.Max;
            aggregate.Sum += temperature;
            aggregate.Count++;
        }

        var result = new List&amp;lt;CityStatistics&amp;gt;();
        foreach ((string city, CityAggregate aggregate) in aggregates)
        {
            result.Add(new()
            {
                City = city,
                MinTemperature = aggregate.Min / 10.0,
                AverageTemperature = Math.Round(aggregate.Sum / 10.0 / aggregate.Count, 1),
                MaxTemperature = aggregate.Max / 10.0
            });
        }

        CityStatistics[] resultAsArray = result.ToArray();

        return resultAsArray;
    }

    private class CityAggregate
    {
        public float Min { get; set; } = float.MaxValue;
        public float Max { get; set; } = float.MinValue;
        public float Sum { get; set; }
        public int Count { get; set; }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;and here an implementation that uses the same structure, but in JavaScript:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;const measurementsEndpoint = &amp;quot;https://kristoffer-strube.dk/API/not-the-measurment-endpoint/&amp;quot;

let responseAsText = undefined;

async function work(input) {
    if (responseAsText == undefined) {
        let response = await fetch(measurementsEndpoint + input);
        responseAsText = await response.text();
    }
    
    let aggregates = new Map();

    for (line of responseAsText.split('\n')) {
        let parts = line.split(';');
        let city = parts[0];
        let temperature = parseFloat(parts[1]);

        let aggregate = aggregates.get(city);
        if (aggregate == undefined) {
            aggregate = { min: Number.MAX_VALUE, max: -Number.MAX_VALUE, sum: 0, count: 0 };
            aggregates.set(city, aggregate);
        }
        aggregate.min = temperature &amp;lt; aggregate.min ? temperature : aggregate.min;
        aggregate.max = temperature &amp;gt; aggregate.max ? temperature : aggregate.max;
        aggregate.sum += temperature;
        aggregate.count++;
    }

    let result = [];

    aggregates.forEach((aggregate, city) =&amp;gt; {
        result.push({
            city: city,
            minTemperature: aggregate.min,
            averageTemperature: Math.round((aggregate.sum / aggregate.count + Number.EPSILON) * 10) / 10,
            maxTemperature: aggregate.max,
        })
    })

    return result;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Common for the two implementations is that they cache the result from the API as a string. We do this for two reasons. We are not interested in comparing how fast JavaScript or .NET can download something in the browser. Secondly, download speed can vary a lot, which would add unnecessary noise to the results or the benchmark. We ran the .NET implementation with AOT compilation as I don't expect it to be comparable with the speed of JS without.&lt;/p&gt;
&lt;p&gt;Let's see the results.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/average-city-temperature-aot.png" alt="Results from measuring the average running time for finding the average temperature of cities with different worker wrappers" /&gt;&lt;/p&gt;
&lt;p&gt;Not the result we were hoping for, but let's look at it anyway. None of the .NET workers were faster than the JS worker. I tried to dig deeper into the code and make some ad-hoc micro-benchmarks of the core parts, and it seems like JS is faster at most of them, even when we AOT compile. Some of the critical parts of performance are the lookups in the dictionary/map and the conversion from string to float. Both of these were faster in JS. My assumption is that the browsers have some efficient implementation that is not written in JS, which they use for these operations. It makes sense that the browser has efficient map implementations, as most browsers use the same underlying implementation for maps and objects. So, every time some attribute of an object in JS is accessed, it essentially equates to a map lookup. Furthermore, I expect that this is optimized for string keys as most attributes on objects are identified by a string key. For conversion from string to float, it also makes sense that the browsers have some efficient implementation they use for making this conversion for any input that is number typed or when parsing JSON with numbers in them.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Now, we have seen how we can make some work in the background using JS and Web Workers. Following that, we have seen 2 of the most popular Blazor wrappers for Web Workers that make it possible to evaluate some .NET code in a background thread. Next, we presented our own novel approach for making a .NET Web Worker, which uses the &lt;code&gt;wasm-experimental&lt;/code&gt; workload. In the end, we compared three different equivalent pieces of work in JS and each of the three wrapper implementations. The first comparison looked at the performance of workloads that depend on large inputs, and for this, we saw that JS was the fastest but that our new approach for Web Workers outperformed the existing implementations. The general picture we saw from this was that there was a 2X overhead from using our Web Worker implementation compared to starting a JS worker from Blazor. Depending on your work, this might still make it worth using our implementation. You might be better equipped to maintain the .NET implementation or might not have an implementation available for JS. The next comparison was on CPU-intensive work. We first saw that JS was still faster, but after turning on AOT compilation for the .NET workers we saw that they all outperformed JS for sufficiently large workloads. Our .NET worker implementation still outperformed the others, but this time, it had the same scaling factor and only won due to a smaller startup overhead. The last benchmark was intended to represent some varied work involving reading and processing data. For this, we again saw that the JS worker was faster than the .NET workers, even with AOT on. We reasoned that JS probably uses some optimized subroutines for some of the most performance-heavy parts, which might be why it outperformed the .NET worker implementations. Even though JS outperformed .NET, it could still make sense to use .NET workers to keep your codebase coherent or if you don't have a JS implementation for the work you need to do. To conclude, using Web Workers in Blazor looks very promising, and I look forward to using this in some projects very soon. If you have any questions related to this article or our Worker implementation, feel free to reach out to me.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">cancelling-long-running-jsinterop-calls</guid><link>https://kristoffer-strube.dk/post/cancelling-long-running-jsinterop-calls/</link><title>Cancelling long-running JSInterop calls</title><description>When making JSInterop calls in Blazor, the invocations can vary in duration depending on what function is invoked. If these invocations take a long time, we want to be able to cancel them if we no longer need to finish the task, i.e., if the user navigates away from the current page or actively cancels some action. It is possible to use the CancellationToken that we are all familiar with from async tasks in .NET, but this only makes it so that deserialization of the response is not handled if the invocation has already started. In this article, we will show how to cancel a JSInterop invocation in the middle of its invocation using the AbortController and AbortSignal types and present how the Blazor.DOM library makes this easy.</description><pubDate>Mon, 29 Apr 2024 00:00:00 +0200</pubDate><a10:updated>2024-04-29T00:00:00+02:00</a10:updated><a10:content type="text">&lt;h3&gt;Parsing a &lt;code&gt;CancellationToken&lt;/code&gt; to invocations&lt;/h3&gt;
&lt;p&gt;As developers who are used to using async-await, we will try to see if an async method takes a &lt;code&gt;CancellationToken&lt;/code&gt; when we want the ability to cancel tasks. We see that the async invocation tasks for &lt;code&gt;JSInterop&lt;/code&gt; actually take a &lt;code&gt;CancellationToken&lt;/code&gt; parameter. But this won't work. Let's check the source code and see what the &lt;code&gt;CancellationToken&lt;/code&gt; is used for. First, we check the parameter description for &lt;code&gt;cancellationToken&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;param name=&amp;quot;cancellationToken&amp;quot;&amp;gt;
A cancellation token to signal the cancellation of the operation. Specifying this parameter will override any default cancellations such as due to timeouts
(&amp;lt;see cref=&amp;quot;DefaultAsyncTimeout&amp;quot;/&amp;gt;) from being applied.
&amp;lt;/param&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;So, from the description, it seems like this makes it possible to cancel the invocation. But to go even deeper down the rabbit hole, let's see what it is used for. First, if the &lt;code&gt;CancellationToken&lt;/code&gt; can be canceled, and this happens, it tries to set the state of the resulting task to canceled and cleans up the task and cancellation callback registration.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;if (cancellationToken.CanBeCanceled)
{
    _cancellationRegistrations[taskId] = cancellationToken.Register(() =&amp;gt;
    {
        tcs.TrySetCanceled(cancellationToken);
        CleanupTasksAndRegistrations(taskId);
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then it does the same if the &lt;code&gt;CancellationToken&lt;/code&gt; had already been cancelled so that we don't need to start the invocation if it was immediately canceled.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;if (cancellationToken.IsCancellationRequested)
{
    tcs.TrySetCanceled(cancellationToken);
    CleanupTasksAndRegistrations(taskId);

    return new ValueTask&amp;lt;TValue&amp;gt;(tcs.Task);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then, it starts the invocation, which means we will have no more opportunities for the task to be stopped if a cancellation is requested before the JS function is done and calls back. If the source of the &lt;code&gt;CancellationToken&lt;/code&gt; was canceled while the invocation happened, then the entire JS function will finish first. Then, when the method that receives the response is invoked, it checks whether the task callback has already been removed and exits early in that case.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;if (!_pendingTasks.TryRemove(taskId, out var tcs))
{
    // We should simply return if we can't find an id for the invocation.
    // This likely means that the method that initiated the call defined a timeout and stopped waiting.
    return false;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is great as we spared the work of deserializing the response, which could have been expensive, as we have seen in our previous post on &lt;a href="https://kristoffer-strube.dk/post/a-holistic-comparison-of-blazor-wasm-performance-from-aspnet-core-5-to-8/"&gt;Blazor performance&lt;/a&gt;. But this still doesn't enable us to cancel JS functions while they are running.&lt;/p&gt;
&lt;h3&gt;Cancelling using the &lt;code&gt;AbortController&lt;/code&gt; and &lt;code&gt;AbortSignal&lt;/code&gt;&lt;/h3&gt;
&lt;p&gt;To support canceling a JS function, we need to parse something into it that it can use to check whether it should be stopped early. JS has a standard equivalent to the &lt;code&gt;CancellationToken&lt;/code&gt; and &lt;code&gt;CancellationTokenSource&lt;/code&gt; types: the &lt;code&gt;AbortSignal&lt;/code&gt; and &lt;code&gt;AbortController&lt;/code&gt; types. If we create an &lt;code&gt;AbortController&lt;/code&gt;, we can get its &lt;code&gt;AbortSignal&lt;/code&gt;. Using the &lt;code&gt;AbortController&lt;/code&gt;, we can then abort the operation, and using the &lt;code&gt;AbortSignal&lt;/code&gt;, we can check whether the operation has been canceled. The introduction of this has a similar history to that of the &lt;code&gt;CancellationToken&lt;/code&gt; as it was not introduced when JS first got support for the async-await pattern. It was instead introduced later when we discovered that some operations would be nice to be able to abort. This also means that it is sadly not used in all APIs, as some APIs were standardized before its introduction. However, a lot of new APIs utilize it to cancel asynchronous operations.&lt;/p&gt;
&lt;p&gt;One API that utilizes the &lt;code&gt;AbortSignal&lt;/code&gt; is the &lt;a href="https://fetch.spec.whatwg.org/"&gt;Fetch API&lt;/a&gt;. The fetch API standardizes ways to fetch resources in JavaScript. In Blazor WASM, the &lt;code&gt;HttpClient&lt;/code&gt; is implemented so that it uses this API to make its requests, but we can also use the API directly without this abstraction. In JS, we can write the following to make a simple GET request:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;async function getCharacterInfo(characterNumber) {
    let response = await fetch(&amp;quot;https://api.sampleapis.com/futurama/characters/&amp;quot; + characterNumber);
    let json = response.json();
    return json;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above sample URL returns some information about a character from Futurama and is actually pretty fast, but we could imagine that it was a slow endpoint and that we would like to abort the fetch request mid-way in some cases. To do this we simple need to specify some options for the optional &lt;code&gt;init&lt;/code&gt; parameter for the fetch method.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;async function getCharacterInfo(characterNumber) {
    let abortController = new AbortController();
    let abortSignal = abortController.signal;

    let requestInit = {
        &amp;quot;signal&amp;quot;: abortSignal
    };

    let response = await fetch(&amp;quot;https://api.sampleapis.com/futurama/characters/&amp;quot; + characterNumber, requestInit);
    let json = response.json();
    return json;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In principle, the above is all we need. But currently, we cannot tell the &lt;code&gt;AbortController&lt;/code&gt; that the action should be canceled as it is not exposed anywhere outside the function. There are multiple ways to achieve this. One way is to save the abort controllers in a map where the key is defined by some extra parameter like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;window.abortControllers = {};

async function getCharacterInfo(characterNumber, abortControllerKey) {
    let abortController = new AbortController();
    window.abortControllers[abortControllerKey] = abortController;
    let abortSignal = abortController.signal;

    let requestInit = {
        &amp;quot;signal&amp;quot;: abortSignal
    };

    let response = await fetch(&amp;quot;https://api.sampleapis.com/futurama/characters/&amp;quot; + characterNumber, requestInit);
    let json = response.json();
    return json;
}

function abortFetch(abortControllerKey) {
    window.abortControllers[abortControllerKey].abort();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above makes it possible to abort a fetch using some &lt;code&gt;abortControllerKey&lt;/code&gt; to identify the operation that should be canceled. But this requires us to remember this arbitrary key and ensure that the key is unique. This also pollutes the global window with the &lt;code&gt;abortControllers&lt;/code&gt; attribute that could potentially collide with attributes used by other libraries. One last problem with this solution is that the &lt;code&gt;AbortController&lt;/code&gt;s would never be garbage collected as they continue to be referenced in the &lt;code&gt;abortControllers&lt;/code&gt; map. To solve that problem, we would need to make a separate third call to remove the reference to the &lt;code&gt;AbortController&lt;/code&gt; once the operation is finished.&lt;/p&gt;
&lt;p&gt;Let's instead use another approach that mitigates these problems. Instead of keeping the &lt;code&gt;AbortController&lt;/code&gt; in a map, we could construct the &lt;code&gt;AbortController&lt;/code&gt; outside the JS function and then parse it in as an &lt;code&gt;IJSObjectReference&lt;/code&gt;. With this approach, our JS functions would look like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;async function getCharacterInfo(characterNumber, abortController) {
    let abortSignal = abortController.signal;

    let requestInit = {
        &amp;quot;signal&amp;quot;: abortSignal
    };

    let response = await fetch(&amp;quot;https://api.sampleapis.com/futurama/characters/&amp;quot; + characterNumber, requestInit);
    let json = response.json();
    return json;
}

function createAbortController() {
    return new AbortController();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This could then be used from Blazor like this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public partial class Index : IDisposable
{
    private string age = &amp;quot;&amp;quot;;
    private int characterNumber = 1;
    private CancellationTokenSource? fetchCancellationTokenSource;

    [Inject]
    public required IJSRuntime JSRuntime { get; set; }

    public async Task UpdateCharacterAge()
    {
        age = &amp;quot;&amp;quot;;

        // Get CancellationToken.
        fetchCancellationTokenSource = new CancellationTokenSource();
        CancellationToken token = fetchCancellationTokenSource.Token;

        // Create AbortController
        await using IJSObjectReference abortController = await JSRuntime.InvokeAsync&amp;lt;IJSObjectReference&amp;gt;(&amp;quot;createAbortController&amp;quot;);

        // Register that it should abort when the CancellationToken is canceled.
        token.Register(async () =&amp;gt;
        {
            await abortController.InvokeVoidAsync(&amp;quot;abort&amp;quot;);
        });

        // Make JS invocation.
        Character character = await JSRuntime.InvokeAsync&amp;lt;Character&amp;gt;(&amp;quot;getCharacterInfo&amp;quot;, token, characterNumber, abortController);

        // Reset the fetchCancellationTokenSource and set result.
        fetchCancellationTokenSource = null;
        age = character.Age;
    }

    public record Character(string Occupation, string Age);

    public void Dispose()
    {
        fetchCancellationTokenSource?.Cancel();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This might seem like a lot of C# code, but I promise that it is less code than what would have been needed for the JS map-based solution that we saw the JS code for earlier. We use the &lt;code&gt;AbortController&lt;/code&gt; and the &lt;code&gt;CancellationTokenSource&lt;/code&gt; we discussed previously. They work nicely together. The &lt;code&gt;AbortController&lt;/code&gt; ensures that we can exit the JS function early. The &lt;code&gt;CancellationTokenSource&lt;/code&gt; ensures we don't use time to deserialize the &lt;code&gt;AbortError&lt;/code&gt; that gets thrown when the JS function is aborted.&lt;/p&gt;
&lt;h3&gt;Using Blazor.DOM&lt;/h3&gt;
&lt;p&gt;In the above solution, we needed to write a function to construct an &lt;code&gt;AbortController&lt;/code&gt;, and we needed to know that it had a function called &lt;code&gt;abort&lt;/code&gt; used for aborting the &lt;code&gt;AbortController&lt;/code&gt;. Another thing to point out is that we parsed the &lt;code&gt;AbortController&lt;/code&gt; itself to the &lt;code&gt;getCharacterInfo&lt;/code&gt; function when it really only needed the &lt;code&gt;AbortSignal&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;I've implemented wrappers for the relevant types used in the above sample in my library &lt;a href="https://github.com/KristofferStrube/Blazor.DOM"&gt;Blazor.DOM&lt;/a&gt;. So let's try to rewrite the above sample to be a bit more strongly typed and minimal. Let's first make some changes to the JS part.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;async function getCharacterInfo(characterNumber, abortSignal) {
    let requestInit = {
        &amp;quot;signal&amp;quot;: abortSignal
    };

    let response = await fetch(&amp;quot;https://api.sampleapis.com/futurama/characters/&amp;quot; + characterNumber, requestInit);
    let json = response.json();
    return json;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;As you can see, we now need a lot less JS. We like this. The main difference is that &lt;code&gt;getCharacterInfo&lt;/code&gt; takes the &lt;code&gt;abortSignal&lt;/code&gt; directly instead of the &lt;code&gt;abortController&lt;/code&gt;. This is nice as we really shouldn't be able to access the controller in the function as we could then potentially abort the controller from within the function, giving unwanted side effects or errors in our C# code. Apart from this, we have also removed the method that constructed an &lt;code&gt;AbortController&lt;/code&gt; as we will no longer need to call this. Now, let's see the C# part.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public partial class Index : IDisposable
{
    private string age = &amp;quot;&amp;quot;;
    private int characterNumber = 1;
    private CancellationTokenSource? fetchCancellationTokenSource;

    [Inject]
    public required IJSRuntime JSRuntime { get; set; }

    public async Task UpdateCharacterAge()
    {
        age = &amp;quot;&amp;quot;;

        // Get CancellationToken.
        fetchCancellationTokenSource = new CancellationTokenSource();
        CancellationToken token = fetchCancellationTokenSource.Token;

        // Create AbortController and AbortSignal
        await using AbortController abortController = await AbortController.CreateAsync(JSRuntime);
        await using AbortSignal abortSignal = await abortController.GetSignalAsync();

        // Register that it should abort when the CancellationToken is canceled.
        token.Register(async () =&amp;gt;
        {
            await abortController.AbortAsync();
        });

        // Make JS invocation.
        Character character = await JSRuntime.InvokeAsync&amp;lt;Character&amp;gt;(&amp;quot;getCharacterInfo&amp;quot;, token, characterNumber, abortSignal);

        // Reset the fetchCancellationTokenSource and set result.
        fetchCancellationTokenSource = null;
        age = character.Age;
    }

    public record Character(string Occupation, string Age);

    public void Dispose()
    {
        fetchCancellationTokenSource?.Cancel();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It has not changed much, apart from the fact that we now use strongly-typed types from the &lt;a href="https://www.nuget.org/packages/KristofferStrube.Blazor.DOM"&gt;KristofferStrube.Blazor.DOM&lt;/a&gt; NuGet package.&lt;/p&gt;
&lt;h3&gt;Other JS functions that can be aborted&lt;/h3&gt;
&lt;p&gt;We have now seen one sample of a JS function that can be aborted and understand how we can parse an &lt;code&gt;AbortSignal&lt;/code&gt; to it to have the option to stop the JS function before it finishes. We also understand how to create this &lt;code&gt;AbortSignal&lt;/code&gt; in C# and signal it to abort. So, to better understand how we can use this, let's see some other samples of JS functions to cancel.&lt;/p&gt;
&lt;h4&gt;Infinite loop&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;async function infiniteLoop(abortSignal) {
    while(!abortSignal.aborted) {
        console.log(&amp;quot;Do some actual work forever in 1 second intervals.&amp;quot;);
        await new Promise(r =&amp;gt; setTimeout(r, 1000));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If we have some infinite loop that ensures that some function is called forever with some interval, we also need to be able to stop the loop. For this, we can use that the &lt;code&gt;AbortSignal&lt;/code&gt; has an attribute that tells us whether the signal has been aborted.&lt;/p&gt;
&lt;h4&gt;Piping ReadableStream&lt;/h4&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;function compressReadableStream(readableStream, abortSignal) {
    let compressionStream = new CompressionStream(&amp;quot;gzip&amp;quot;);
    let compressed = readableStream.pipeThrough(compressionStream, { &amp;quot;signal&amp;quot;: abortSignal });
    return compressed;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another API that incorporates the &lt;code&gt;AbortSignal&lt;/code&gt; is the &lt;a href="https://streams.spec.whatwg.org/"&gt;Streams API&lt;/a&gt;. When we pipe some readable stream to a writable stream or through a transformer, we can parse options to the function, enabling us to abort it. This is a good use case as a &lt;code&gt;ReadableStream&lt;/code&gt; could potentially be large or even infinite.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we explored the source code of JSInterop to see what it does when we parse a &lt;code&gt;CancellationToken&lt;/code&gt; to it. We introduced the &lt;code&gt;AbortController&lt;/code&gt; and &lt;code&gt;AbortSignal&lt;/code&gt; types and used them together with the Fetch API to cancel a potentially long-lived operation. Then we saw how we can use the &lt;a href="https://github.com/KristofferStrube/Blazor.DOM"&gt;Blazor.DOM&lt;/a&gt; library to make some of this easier and simpler. In the end, we quickly took a look at some other scenarios where we could use &lt;code&gt;AbortSignal&lt;/code&gt;s. If you have any questions related to the article or use cases I did not cover, feel free to reach out to me.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">a-holistic-comparison-of-blazor-wasm-performance-from-aspnet-core-5-to-8</guid><link>https://kristoffer-strube.dk/post/a-holistic-comparison-of-blazor-wasm-performance-from-aspnet-core-5-to-8/</link><title>A holistic comparison of Blazor WASM performance from ASP.NET Core 5 to 8</title><description>Every year, many small improvements are made to .NET and ASP.NET Core that combined make the framework perform even better. Understanding what these improvements will mean in everyday use cases can be difficult. We could dissect all the improvements individually and make statements like "Indexing in arrays has become 4000% faster when you do X!" or some other absurd comparison. But what I find just as interesting is how much faster some common tasks consisting of many operations executed in a concrete context have become. In this article, we will attempt to compare the performance of a varied set of tasks in Blazor WASM with and without AOT from ASP.NET Core 5 up to 8.</description><pubDate>Thu, 25 Jan 2024 00:00:00 +0100</pubDate><a10:updated>2024-01-25T00:00:00+01:00</a10:updated><a10:content type="text">&lt;h3&gt;The sample project&lt;/h3&gt;
&lt;p&gt;I have tried to make a sample project that represents some common work. For this, I started with my relatively new &lt;a href="https://github.com/KristofferStrube/DocumentSearching"&gt;DocumentSearching&lt;/a&gt; repository that implements a Suffix Trie for document searching. This is one of the simplest approaches for indexing text so that it is faster to search for all indexes of any substring in an input string. Below is a demo of what using this index can look like. You can try out this demo yourself here: &lt;a href="https://kristofferstrube.github.io/DocumentSearching/"&gt;https://kristofferstrube.github.io/DocumentSearching/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;video width="800px" src="https://kristoffer-strube.dk/videos/search-periodic-table.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;The implementation is pretty naive (let's say that's intentional), so constructing the index reads through the input text many times, making it a rather intensive operation. Since the construction is expensive, it will make sense to save the constructed index somewhere in some cases so that it simply needs to be read to be ready for use. Inspired by this use case, I made a series of 7 tasks representing some common tasks.&lt;/p&gt;
&lt;h4&gt;Read CSV&lt;/h4&gt;
&lt;p&gt;We need some text to search in before constructing the search index itself. For this, I have found a CSV file containing descriptions for all the elements of the Periodic Table as a sample of something to search through. We will load this file using a &lt;code&gt;HttpClient&lt;/code&gt; and then parse it using &lt;a href="https://joshclose.github.io/CsvHelper/"&gt;CsvHelper&lt;/a&gt;. To limit the size of this test, we will only load the descriptions of the first 10 elements. The summaries of the first 10 elements consist of 3026 characters. We place a scope around this part so that the &lt;code&gt;StreamReader&lt;/code&gt; and &lt;code&gt;CsvReader&lt;/code&gt; get disposed of before measuring the time.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;{
    Stream periodicTableCsv = await HttpClient.GetStreamAsync(&amp;quot;data/periodic-table-detailed.csv&amp;quot;);
    using StreamReader streamReader = new StreamReader(periodicTableCsv);
    using CsvReader csv = new(streamReader, CultureInfo.InvariantCulture);
    elements = csv.GetRecords&amp;lt;Element&amp;gt;().Take(10).ToArray();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Construct Index&lt;/h4&gt;
&lt;p&gt;Using the entries from the CSV file, we will then construct the search index using the &lt;code&gt;DocumentIndex&lt;/code&gt; class from the repository. I have made a branch of the repository where the class library targets .NET 5 so that it can be used in all the versions we run tests for. The branch is here: &lt;a href="https://github.com/KristofferStrube/DocumentSearching/tree/experiment/aspnetcore5"&gt;DocumentSearching/experiment/aspnetcore5&lt;/a&gt;. With the &lt;code&gt;DocumentIndex&amp;lt;Element&amp;gt;.Create&lt;/code&gt; method, we create an index over all the summary for each element. We lower the summary text so that we don't need to think about the casing as long as we also lower our search query.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;index = DocumentIndex&amp;lt;Element&amp;gt;.Create(elements, e =&amp;gt; e.Summary.ToLower());
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;JSON Serialize&lt;/h4&gt;
&lt;p&gt;We will then serialize the search index. The Suffix Trie, the index's main part, consists of a large hierarchy of &lt;code&gt;Node&lt;/code&gt; classes that point to each other. This is not unlike the very large models that some send between their clients and servers when the users work with complex models.&lt;/p&gt;
&lt;p&gt;We define the following serialization options object outside the main test loop for this and the deserialization.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private readonly JsonSerializerOptions serializerOptions = new()
{
    ReferenceHandler = ReferenceHandler.Preserve
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then use it like this when serializing:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;string serialized = JsonSerializer.Serialize(index, serializerOptions);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Write to LocalStorage&lt;/h4&gt;
&lt;p&gt;Next, we need to save it somewhere. We will use JSInterop to write the serialized index to LocalStorage for this. This is also a common part of many Blazor WASM applications that rely on APIs from the browser through JSInterop to deliver a rich experience.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;JSRuntime.InvokeVoid(&amp;quot;window.localStorage.setItem&amp;quot;, &amp;quot;index&amp;quot;, serialized);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Read from LocalStorage&lt;/h4&gt;
&lt;p&gt;In a real use case, we could imagine that we had left the site and revisited it. So, we need to read the serialized index back from LocalStorage. For this, we likewise use JSInterop to read the string back. I don't expect this to be very different from the writing to LocalStorage, but it completes the story.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;string readIndex = JSRuntime.Invoke&amp;lt;string&amp;gt;(&amp;quot;window.localStorage.getItem&amp;quot;, &amp;quot;index&amp;quot;);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;JSON Deserialize&lt;/h4&gt;
&lt;p&gt;Now, we reconstruct the search index from the previously serialized string. It is not uncommon to talk with some JSON-based API in a Blazor WASM application, which also involves a lot of deserialization. Depending on how data-driven your site is, this might be a hot-path in your application.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;DocumentIndex&amp;lt;Element&amp;gt; deserializedIndex = JsonSerializer.Deserialize&amp;lt;DocumentIndex&amp;lt;Element&amp;gt;&amp;gt;(readIndex, serializerOptions);
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Search&lt;/h4&gt;
&lt;p&gt;With the index back, we can perform some searches in the elements. I have chosen to search for 10 words that I know appear in the summaries of the first 10 elements. The search is so fast that the &lt;code&gt;Stopwatch&lt;/code&gt; class I'm using for my timings wouldn't be able to measure 10 searches alone, so I'm putting these 10 searches in a loop that iterates 10 times so that we have something comparable. When searching, the index does a lot of equality checks and lookups in arrays to find the matching indexes, which could mimic the work done in applications that evaluate a lot of business logic on the client side.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;for (int j = 0; j &amp;lt; 10; j++)
{
    searchResults = deserializedIndex.ExactSearch(&amp;quot;hydrogen&amp;quot;);
    searchResults = deserializedIndex.ExactSearch(&amp;quot;oxygen&amp;quot;);
    searchResults = deserializedIndex.ExactSearch(&amp;quot;light&amp;quot;);
    searchResults = deserializedIndex.ExactSearch(&amp;quot;heavy&amp;quot;);
    searchResults = deserializedIndex.ExactSearch(&amp;quot;chemical&amp;quot;);
    searchResults = deserializedIndex.ExactSearch(&amp;quot;element&amp;quot;);
    searchResults = deserializedIndex.ExactSearch(&amp;quot;symbol&amp;quot;);
    searchResults = deserializedIndex.ExactSearch(&amp;quot;atomic&amp;quot;);
    searchResults = deserializedIndex.ExactSearch(&amp;quot;number&amp;quot;);
    searchResults = deserializedIndex.ExactSearch(&amp;quot;weight&amp;quot;);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Testing&lt;/h3&gt;
&lt;p&gt;I ran the steps above in their natural order inside a loop that repeated 110 times. I timed how fast each part took using the &lt;code&gt;Stopwatch&lt;/code&gt; class by starting one and then measuring how many milliseconds passed. After collecting 110 measurements for each part, I removed the first and last 5 measurements and calculated the minimum, average, and maximum values. This is not a recommendation for how to make benchmarks for Blazor WASM, as many factors can influence the results when other work is done on the computer simultaneously. But it is better than simply measuring the time once.&lt;/p&gt;
&lt;p&gt;The following is the main loop that we will execute in our tests:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private Element[] elements = default!;
private DocumentIndex&amp;lt;Element&amp;gt; index = default!;
private SearchResult&amp;lt;Element&amp;gt;[] searchResults = [];

public async Task Start()
{
    for (int i = 0; i &amp;lt; 110; i++)
    {
        // Read CSV

        // Construct Index

        // JSON Serialize Index

        // Write to LocalStorage

        // Read from LocalStorage

        // JSON Deserialize Index

        // Search for words in summaries.

        // Cleanup
        elements = default!;
        index = default!;
        JSRuntime.InvokeVoid(&amp;quot;window.localStorage.clear&amp;quot;);
        searchResults = [];
        await Task.Delay(200);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;At the end of every loop cycle, we also do some cleanup. Most of it is simple: returning the intermediate variables to their initial values. We also clear LocalStorage so it is empty at the start of every iteration. We also make a 200-millisecond delay. This is so that the runtime gets time to run any garbage collection it needs to perform and to ensure that the UI does not become unresponsive when using the single thread available in Blazor WASM for long periods.&lt;/p&gt;
&lt;p&gt;After running the loop, we get the aforementioned statistics from all the collections of measurements. We do this by parsing the collections to the following method that formats the statistics we are interested in before we output them for each part of the loop.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private string GetStatistics(List&amp;lt;double&amp;gt; timings)
{
    double[] middleTimings = timings.Skip(5).SkipLast(5).ToArray();

    double min = Math.Round(middleTimings.Min(), 2);
    double average = Math.Round(middleTimings.Average(), 2);
    double max = Math.Round(middleTimings.Max(), 2);

    return $&amp;quot;Min: {min} ms; Average: {average} ms; Max: {max} ms;&amp;quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I will use the fastest of the 100 measurements in the plots I make later, so it wasn't necessary to throw away the first and last 5 as they could only be slower than the rest depending on how the runtime optimizes the loop. But it still makes sense to skip these to get a more meaningful insight into a long-running process's average and maximum values. Here is an example of the minimal, average, and maximal measurements made for the test project targeting ASP.NET Core 5 with .NET 5 published with the standard release configuration:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;Read CSV: Min: 5.8 ms; Average: 9.77 ms; Max: 29.3 ms;
Construct Index: Min: 5.7 ms; Average: 6 ms; Max: 7.9 ms;
JSON Serialize: Min: 331 ms; Average: 336.53 ms; Max: 349.2 ms;
Write LocalStorage: Min: 208.4 ms; Average: 211.46 ms; Max: 220.8 ms;
Read LocalStorage: Min: 239.4 ms; Average: 242.46 ms; Max: 255.8 ms;
JSON Deserialize: Min: 1041.7 ms; Average: 1052.56 ms; Max: 1093.3 ms;
Search: Min: 12.5 ms; Average: 12.87 ms; Max: 17.3 ms;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I had expected that the measurements would vary a lot more. If we look at the proximity of the min values and average values, we see that they are generally pretty close. Without doing any statistical analysis, this tells me that most measurements are close to the minimum value.&lt;/p&gt;
&lt;h3&gt;Test scenarios&lt;/h3&gt;
&lt;p&gt;I started by making a sample Blazor WASM project that targeted .NET 5. In this, I created a page with the above methods defined. It is really easy to upgrade a Blazor WASM project from .NET 5 and ASP.NET Core 5 up to 8. It is just to update the versions of the Blazor-specific NuGet packages to match the selected .NET version. Apart from this, we also want to try out AOT for all the versions of Blazor WASM that support it. Actually, of the versions we want to test, only Blazor in ASP.NET Core 5 didn't have it yet. But that is fine, as it simply re-iterates how great a jump AOT was when introduced in .NET 6. To configure the application to use AOT, we only need to add the following to the csproj:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-xml"&gt;&amp;lt;RunAOTCompilation&amp;gt;true&amp;lt;/RunAOTCompilation&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we are ready to run our tests. We are going to run tests on the following configurations:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.NET 5 - ASP.NET Core 5.0.17 Release&lt;/li&gt;
&lt;li&gt;.NET 6 - ASP.NET Core 6.0.26 Release&lt;/li&gt;
&lt;li&gt;.NET 6 - ASP.NET Core 6.0.26 Release AOT&lt;/li&gt;
&lt;li&gt;.NET 7 - ASP.NET Core 7.0.15 Release&lt;/li&gt;
&lt;li&gt;.NET 7 - ASP.NET Core 7.0.15 Release AOT&lt;/li&gt;
&lt;li&gt;.NET 8 - ASP.NET Core 8.0.0 Release&lt;/li&gt;
&lt;li&gt;.NET 8 - ASP.NET Core 8.0.0 Release AOT&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;You can find the ASP.NET Core 5 base project that we will upgrade to each of the above configurations here: &lt;a href="https://github.com/KristofferStrube/DocumentSearching/tree/experiment/aspnetcore5/samples/KristofferStrube.DocumentSearching.BlazorWasm5"&gt;KristofferStrube.DocumentSearching.BlazorWasm5&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Results&lt;/h3&gt;
&lt;p&gt;And now for the results! I have made some plots presenting the results for each part,&lt;/p&gt;
&lt;h4&gt;Read CSV&lt;/h4&gt;
&lt;p&gt;Reading the CSV had some improvement when moving to ASP.NET Core 6, but it did not seem like AOT has a large impact in this version. Surprisingly, ASP.NET Core 7 performed worse but did have another great jump in performance for AOT. In  ASP.NET Core 8, we got the standard release down to the same level that we had in 6 and matched the AOT level of 7, so it seems like the greatest of both versions. Generally, there are not any huge improvements, which makes sense as there are other network-related constraints to reading a file fast with a &lt;code&gt;HttpClient&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/read-csv.png" alt="Plot showing the performance of reading CSVs in different versions of Blazor WASM" /&gt;&lt;/p&gt;
&lt;h4&gt;Construct Index&lt;/h4&gt;
&lt;p&gt;Constructing the search index also improved when moving to ASP.NET Core 6. Though, again not a huge difference with and without AOT. Then, at ASP.NET Core 7, we see a huge improvement for AOT and again see an actual regression in the standard release. As for ASP.NET Core 8, we again remain around the same level for AOT but have a nice catchup for the non-AOT that is now twice as fast as the ASP.NET Core 6 AOT version.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/construct-index.png" alt="Plot showing the performance of constructing a search index in different versions of Blazor WASM" /&gt;&lt;/p&gt;
&lt;h4&gt;JSON Serialize&lt;/h4&gt;
&lt;p&gt;Serializing the search index seems to have followed an almost identical pattern of improvements. I had expected this to have a much greater percentage improvement as we have heard a lot about the improvements made to System.Text.Json throughout every release. But if we instead look at the absolute improvement of the task, this is a pretty impressive improvement, cutting a serialization task that previously took more than 300 milliseconds, which is a great enough delay for a person to perceive it, down to less than 25 milliseconds in ASP.NET Core 8 with AOT.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/json-serialize.png" alt="Plot showing the performance of JSON serializing in different versions of Blazor WASM" /&gt;&lt;/p&gt;
&lt;h4&gt;Write LocalStorage&lt;/h4&gt;
&lt;p&gt;Again, it is a similar picture to what we had in the two other parts when we look at the performance of writing to LocalStorage. The one noticable thing is that writing to LocalStorage in ASP.NET Core 6 AOT is much better than its non-AOT counterpart. So, it seems that JSInterop is especially receptive to AOT's optimizations.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/write-localstorage.png" alt="Plot showing the performance of writing to LocalStorage with JSInterop in different versions of Blazor WASM" /&gt;&lt;/p&gt;
&lt;h4&gt;Read LocalStorage&lt;/h4&gt;
&lt;p&gt;I had expected that returning values from JSInterop would be consistently slower than writing as we need to allocate a new string. But it seems like they are close to being equally matched. The one surprising thing to notice is that reading in ASP.NET Core 6 AOT is more than twice as fast as writing. I will definitely have this in the back of my mind when I make systems that are JSInterop intensive in the future.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/read-localstorage.png" alt="Plot showing the performance of reading from LocalStorage with JSInterop in different versions of Blazor WASM" /&gt;&lt;/p&gt;
&lt;h4&gt;JSON Deserialize&lt;/h4&gt;
&lt;p&gt;The JSON deserialization is the slowest of all the parts. I assume that this is because deserialization relies heavily on reflection combined with some good old string parsing. That we start off with having the deserialization take more than a second in ASP.NET Core 5 only makes it even more impressive that we got down below 50ms in the ASP.NET Core 8 AOT release.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/json-deserialize.png" alt="Plot showing the performance of JSON deserializing in different versions of Blazor WASM" /&gt;&lt;/p&gt;
&lt;h4&gt;Search&lt;/h4&gt;
&lt;p&gt;And now to the final part that all the previous steps have led to. The search itself. In ASP.NET Core 6, AOT was slower than the non-AOT counterpart, which is a contradiction to what almost seems like a rule from the previous plots. But overall, they still follow the pattern we have seen in the other plots, i.e., that non-AOT has gotten better through all versions except ASP.NET Core 7 and that the AOT version gets better over time, where the one to 7 is the greatest jump.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/search.png" alt="Plot showing the performance of searching with constructed search index in different versions of Blazor WASM" /&gt;&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Now, we have seen how fast some common tasks are performed in Blazor WASM with different versions of ASP.NET Core. A couple of tests did not have the outcome I had expected before I started the experiments. The greatest surprise is how ASP.NET Core 7 non-AOT was worse than 6 in almost all tasks. I want to explore the source of this in the future as it seems like something others should have reported on before me. Apart from this, I also found that JSON is not a valid serialization mechanism for the sample use case we created, as it was the limiting part across all the different ASP.NET Core versions we tested. But it was okay as a sample. I can't wait to see what improvements we will see in ASP.NET Core 9 when we get the first previews pretty soon. I do a lot of JSInterop, so I especially hope for more improvements to that both for AOT and non-AOT, as only Blazor WASM can employ AOT.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">reading-notes-from-performance-improvements-in-dotnet-8</guid><link>https://kristoffer-strube.dk/post/reading-notes-from-performance-improvements-in-dotnet-8/</link><title>Reading notes from Performance Improvements in .NET 8</title><description>For the last couple of years, Stephen Toub has posted about the performance improvements coming to the next release of .NET. People have jokingly called these posts "books" because they are very long, and this year is no exception. This year, his post on .NET 8 is 221 pages if you print the entire post with no margins and it's packed with interesting details. At my current day job, I work with practical data-intensive systems in .NET where improvements like the ones Stephen Toub presents can have a huge impact. In this post, I will share my reading notes from Stephen Toub's post on Performance Improvements in .NET 8 to give you a quick insight into what I found interesting. I especially found the improvements made to the JIT compiler interesting, so I will primarily touch on those.</description><pubDate>Tue, 26 Sep 2023 00:00:00 +0200</pubDate><a10:updated>2023-09-26T00:00:00+02:00</a10:updated><a10:content type="text">&lt;p&gt;I will only summarize and explain the parts that I found especially useful or interesting. That means I have not covered all parts of his post. If you want an overview of all improvements, then I very much recommend that you read his full post: &lt;a href="https://devblogs.microsoft.com/dotnet/performance-improvements-in-net-8/"&gt;Performance Improvements in .NET 8 by Stephen Toub - MSFT&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Dynamic PGO&lt;/h3&gt;
&lt;p&gt;I think the greatest improvement to .NET 8 is Dynamic PGO (Profile-Guided Optimization). The concept is not new to .NET 8, but .NET 8 is the first version where Dynamic PGO is turned on by default. So, what we get now in .NET 8 is actually the result of all the improvements and refinements made to Dynamic PGO since it was first introduced as a preview feature in .NET 6.&lt;/p&gt;
&lt;p&gt;In broad terms, Dynamic PGO enables the JIT (Just-In-Time) compiler to observe which parts of the program are run the most while the program is running in production. It can then re-compile the parts that get run the most to make them more efficient when they are run in the future. You might ask, &lt;em&gt;&amp;quot;Why not compile all parts of the program to be the most efficient before it starts?&amp;quot;&lt;/em&gt;. The answer to this question has multiple facets.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Long startup time&lt;/strong&gt;:
If the JIT compiler should try to compile all parts of the program before the program starts, then you might experience a very long startup time. If you have ever tried to run an AOT (Ahead Of Time) compilation of a program, then you know this can take a very long time. And the worst part is that it would use valuable time on compiling methods to be effective even though you might only run it once.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Loss of insight&lt;/strong&gt;:
When the program has already started, the JIT can get a greater insight into the actual values of certain fields and make smarter decisions. Stephen Toub gives an example of a &lt;code&gt;static readonly bool&lt;/code&gt; which, once initialized, gets some value that could then be handled as a &lt;code&gt;const&lt;/code&gt; when that code gets re-compiled, making the access to that faster while opening up for constant-inlining in places that it is used.&lt;/p&gt;
&lt;p&gt;But how does it know what parts of the code get hit the most? They count how many times each method (and some other smaller parts) gets called. The JIT adds some instrumentation to all methods when it makes its first non-optimized compilation. This instrumentation is simply some code that increments by &lt;code&gt;1&lt;/code&gt; every time that code is called. Then if this count hits some threshold for a method, the JIT will take the time to re-compile this to be more effective. A rather ingenious detail to this is that it also works when you work with multiple threads. Normally, when you try to increment the same number from multiple threads, we either get race conditions, meaning some of the increments will be lost, or we need to take a lock before reading and writing the number, which causes a huge performance penalty as the threads will have to wait for each other.&lt;/p&gt;
&lt;p&gt;They solved this using randomization. They start by using the lock method, but then once the count surpasses &lt;code&gt;8192&lt;/code&gt; (&lt;code&gt;2^13&lt;/code&gt;), they switch to the non-locking method but only does the increment &lt;code&gt;50%&lt;/code&gt; of the time decided by a random source but then increase it with &lt;code&gt;2&lt;/code&gt; instead of &lt;code&gt;1&lt;/code&gt; when it happens to minimize the chance of a race condition happening. Once it increases above &lt;code&gt;16384&lt;/code&gt; (&lt;code&gt;2^14&lt;/code&gt;) they do this once more, changing the probability of the increment happening to &lt;code&gt;25%&lt;/code&gt; with the increment step being &lt;code&gt;4&lt;/code&gt;. This continues for larger and larger numbers. This maintains a rather precise estimation of the actual count with minimal delay and performance penalty.&lt;/p&gt;
&lt;h3&gt;GDV (Guarded Devirtualization)&lt;/h3&gt;
&lt;p&gt;The above counting trick is not only used to check which methods are called the most. It also checks which concrete types are used most often in places where virtual and interface types are used. The JIT then uses this information to generate a &lt;em&gt;fast path&lt;/em&gt; for that concrete type. A fast path means that it adds a direct check for that type and then makes the invocation directly using that type if the type matches, and else makes the implementation lookup as normal. That's why it's called &lt;em&gt;Guarded&lt;/em&gt; as we first guard for that common type. It does this for the single most used type, but it is possible to set the number of direct guards checks to another limit by setting the environment variable &lt;code&gt;DOTNET_JitGuardedDevirtualizationMaxTypeCheck&lt;/code&gt;.&lt;/p&gt;
&lt;h3&gt;Vectorization&lt;/h3&gt;
&lt;p&gt;A trend over the last couple of .NET versions has been improvements to Vectorization. * drom rolls * and the trend continues!&lt;/p&gt;
&lt;p&gt;Vectorization is when you utilize your hardware to do the same calculation to multiple numbers at once. .NET 7 added support for doing this for &lt;code&gt;128&lt;/code&gt; and &lt;code&gt;256&lt;/code&gt; bits at once. &lt;code&gt;256&lt;/code&gt; bits are equivalent to &lt;code&gt;8&lt;/code&gt; ints, so we could execute so-called SIMD (Single Instruction/Multiple Data) instructions on &lt;code&gt;8&lt;/code&gt; ints, all in the same CPU cycle if the machine supports this. In .NET 8 they added support for vectorization of &lt;code&gt;512&lt;/code&gt; bits, which means that we can make certain SIMD operations on &lt;code&gt;16&lt;/code&gt; ints at once if the machine supports it. This means the execution time for simple arithmetic on large data arrays can be halved if the machine supports it. In .NET 7 these improvements were also utilized in the .NET standard &lt;code&gt;System.Linq&lt;/code&gt; methods &lt;code&gt;Sum&lt;/code&gt;, &lt;code&gt;Average&lt;/code&gt;, &lt;code&gt;Min&lt;/code&gt;, and &lt;code&gt;Max&lt;/code&gt;. The &lt;code&gt;512&lt;/code&gt; bit vectorization has likewise been added to these implementations in .NET 8 to make them even more efficient.&lt;/p&gt;
&lt;h3&gt;Branching&lt;/h3&gt;
&lt;p&gt;We say that the program branches whenever a program has multiple paths depending on some condition. The CPU tries to predict which way the program will branch depending on what it thinks is most likely and then continues decoding the instructions on the most likely path while the branch outcome is being evaluated. This is called branch prediction. When branch prediction fails, it can be expensive. To get around this, we want to do branch-free code so that the branch prediction can never fail (as there will be none). The JIT can now emit conditional move instructions, making branch-free code much easier. Specifically, it now recognizes the pattern &lt;code&gt;(b ? 1 : 0)&lt;/code&gt; where &lt;code&gt;b&lt;/code&gt; is a boolean expression to be branchless. If we look at the following method:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private int ModifyForRelevance(int score, bool isNew)
{
    if (isNew)
    {
        return score * 3;
    }
    else
    {
        return score * 2;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This could be rewritten to the following to be branch-free in .NET 8:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private int ModifyForRelevance(int score, bool isNew)
{
    return (isNew ? 1 : 0) * score * 3 + (isNew ? 0 : 1) * score * 2;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;But that the code is branch-free doesn't mean it is more efficient. The first version of the method would generate a lot less code and would need fewer calculations, as we will always calculate both branches in the second version. So, for now, you probably shouldn't go out and use the &lt;code&gt;(b ? 1 : 0)&lt;/code&gt; pattern in all parts of your code. This can be useful, though, when making methods that always take the same amount of time to execute. I don't think Stephen Toub touches on concrete use cases for this. But one such use case is to make constant-time implementations of cryptographic algorithms to prevent timing attacks.&lt;/p&gt;
&lt;h3&gt;Bounds Checking&lt;/h3&gt;
&lt;p&gt;When accessing arrays, the JIT generates checks for the bounds of the arrays that throw the well-known &lt;code&gt;IndexOutOfRangeException&lt;/code&gt; if you read outside the array by accident. But sometimes we know that the index we access can't possibly be outside the bounds. In .NET 8 the JIT understands more of these cases so that unnecessary checks are omitted from the emitted IL code. Stephen Toub gives multiple realistic examples of this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private int GetBucket(int[] buckets, int hashcode) =&amp;gt; buckets[(uint)hashcode % buckets.Length];
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The above indexes into an array using a large hash that might be larger than the actual size of the array. They use the modulus operator to get it within the bounds to get around this. This also means that it can never get outside the bounds, so the JIT doesn't need to generate the code that throws the &lt;code&gt;IndexOutOfRangeException&lt;/code&gt;. Here is another example that has benefited from this:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private bool IsQuoted(string s) =&amp;gt; s.Length &amp;gt;= 2 &amp;amp;&amp;amp; s[0] == '&amp;quot;' &amp;amp;&amp;amp; s[^1] == '&amp;quot;';
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method first checks that the string is longer than &lt;code&gt;2&lt;/code&gt; characters, as we can't have a quoted string shorter than &lt;code&gt;2&lt;/code&gt; characters. In .NET 7 the JIT already recognized that the next sub-expression wouldn't need to make a bound check as it knew that the &lt;code&gt;0&lt;/code&gt; index is safe to access when the array is &lt;code&gt;2&lt;/code&gt; characters long. The new thing in .NET 8 is that it also recognizes that the last expression doesn't need bound checks as it knows that accessing the last element in the array is safe as there only needs to be at least &lt;code&gt;1&lt;/code&gt; element in the array for this to be valid.&lt;/p&gt;
&lt;h3&gt;Constant Folding&lt;/h3&gt;
&lt;p&gt;Constant folding is when the JIT can take an expression that only consists of constants and precompute the expression as it will always give the same result. .NET 8 has made many improvements to constant folding that make it able to recognize more types as being constant, and then employ the folding on these. From what I gathered from the post the most important feature is the support for constant folding of the lengths of &lt;code&gt;ReadOnlySpan&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;, and any type of array. This plays nicely together with the bounds-checking improvements as the JIT can now inline the length of a constant string as a constant itself which again means that it can access parts of the string within its bounds before even compiling, essentially compacting the whole expression to its result before emitting the IL code for that part. The following is a somewhat simple sample:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private static readonly string cat = &amp;quot;CAT&amp;quot;;

private bool CatEndsWithT() =&amp;gt; cat.Length == 3 &amp;amp;&amp;amp; (cat[^1]  | 0x20) == 't';
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;CatEndsWithT&lt;/code&gt; method would previously have needed to check for the length of the string &lt;code&gt;cat&lt;/code&gt; both in the first sub-expression and when accessing the last element in the string. But now in .NET 8 the entire method can return &lt;code&gt;true&lt;/code&gt; without looking at the string because of constant folding.&lt;/p&gt;
&lt;h3&gt;Non-GC Heap&lt;/h3&gt;
&lt;p&gt;.NET 8 introduces a new heap, the Non-GC Heap. It is not something that most developers will use explicitly, but the JIT will use it in multiple scenarios. The Non-GC Heap is a heap parallel to the Garbage Collected heap, which the Garbage Collector will not manage. It is intended for memory that is not going to change throughout the lifetime of the application. .NET uses this to store constants and literals so that the JIT can point to them directly in the Non-GC Heap instead of first having to find out where it is in the normal heap as that can be re-arranged when the Garbage Collector cleans up memory. This means that the following things can now be referenced directly, which saves one move instruction:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;String Literals&lt;/li&gt;
&lt;li&gt;&lt;code&gt;typeof(T)&lt;/code&gt; for types that are not dynamically loaded.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Array.Empty&amp;lt;T&amp;gt;()&lt;/code&gt; again for types not dynamically loaded.&lt;/li&gt;
&lt;li&gt;Value types in static fields (which were previously boxed).&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;Closing notes&lt;/h3&gt;
&lt;p&gt;Now, we have seen the improvements I found most insightful from Stephen Toub's post. And I restate that you should read his full post to get all the details. There were, of course, parts that I didn't cover in this post. Many of the areas I didn't cover focus on improvements to different kinds of arrays/collections and common actions involving these. I would especially like to use spans more, as they bring many benefits. And obviously, I'm also very interested in the improvements that influence Blazor WASM. But I think I will cover that in another post.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">blazor-svgeditor-released</guid><link>https://kristoffer-strube.dk/post/blazor-svgeditor-released/</link><title>Blazor.SVGEditor: Released</title><description>One of my first big Open Source projects was my Blazor SVG Editor. This project lets users edit SVG definitions in a graphical interface, all from Blazor. The users can edit and navigate existing SVGs from the editor using common user interactions like panning, zooming, translating, and scaling. They can likewise change details of the SVG elements that don't have a natural mapping and create new elements using an extensive context menu. I've seen multiple projects that have used the project or parts of it to construct some cool interactive UIs. So I figured I wanted to make it possible for more people to use it by releasing it as a component library on NuGet. In this article, we will look at what features the Blazor SVG Editor NuGet package brings out of the box and how you can extend the functions of the base library to make your own interactive graphical tool.</description><pubDate>Wed, 26 Jul 2023 00:00:00 +0200</pubDate><a10:updated>2023-07-26T00:00:00+02:00</a10:updated><a10:content type="text">&lt;h3&gt;Features&lt;/h3&gt;
&lt;p&gt;Before we look at how we can use the package, we will go through some of the central features of the editor, accompanied by some small videos that show those features.&lt;/p&gt;
&lt;p&gt;You can find the project on GitHub: &lt;a href="https://github.com/KristofferStrube/Blazor.SVGEditor"&gt;https://github.com/KristofferStrube/Blazor.SVGEditor&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can find the package on NuGet: &lt;a href="https://www.nuget.org/packages/KristofferStrube.Blazor.SVGEditor"&gt;KristofferStrube.Blazor.SVGEditor&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And you can demo all the below features in the online demo site: &lt;a href="https://kristofferstrube.github.io/Blazor.SVGEditor/"&gt;https://kristofferstrube.github.io/Blazor.SVGEditor/&lt;/a&gt;&lt;/p&gt;
&lt;h4&gt;Setting input and getting updates&lt;/h4&gt;
&lt;p&gt;The editor parses the XML structure that SVGs are defined in to be able to edit every detail of an SVG. To make it possible for the library consumer to populate this initial value, the &lt;code&gt;Input&lt;/code&gt; is exposed as a Parameter that needs to be set when the component is used. The component has another Parameter &lt;code&gt;InputUpdated&lt;/code&gt; that you can set to listen for when changes happen to the underlying SVG code. This can be used to get a live view of the underlying code while you edit it.&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-listen-to-changes.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h4&gt;Editing shapes&lt;/h4&gt;
&lt;p&gt;The editor enables us to update all shapes. Among these shapes are lines, polygons, rectangles, and paths. Paths are especially complex to parse as they contain many instructions that can be used in many combinations.&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-edit-shapes.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h4&gt;Creating new shapes&lt;/h4&gt;
&lt;p&gt;The editor supports creating new shapes through its context menu and supports unique flows for easy creation for each of these shapes.&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-create-shapes.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h4&gt;Panning and zooming&lt;/h4&gt;
&lt;p&gt;Sometimes some fine details in the SVG would be easier to edit up close either because they are very small or because you need high precision. To support this, we enable you to zoom and move around the canvas using the scroll wheel and the mouse's middle button. These interactions are only supported for desktop as they rely on you having a mouse or at least some way to scroll. If you want to contribute to the project, then I have an open issue to add support for touch devices: &lt;a href="https://github.com/KristofferStrube/Blazor.SVGEditor/issues/11" target="_blank"&gt;Blazor.SVGEditor Issue #11&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-panning-and-zooming.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h4&gt;Multi-select and area selection&lt;/h4&gt;
&lt;p&gt;The editor also enables the user to select multiple elements when editing by holding the &lt;code&gt;CTRL&lt;/code&gt; key down while selecting. To select multiple items in an area, the user can hold down the left mouse button and drag to mark the desired elements, similar to selecting multiple files in the Windows file explorer.&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-multi-select.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h4&gt;Color selection&lt;/h4&gt;
&lt;p&gt;Using the context menu, the users can change the fill color of all shapes by picking a color from a modal. They can likewise change the color of the strokes (the outline of shapes) and other details related to the stroke.&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-color-selection.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h4&gt;Grouping and Ungrouping&lt;/h4&gt;
&lt;p&gt;SVG elements can be grouped using the &lt;code&gt;&amp;lt;g&amp;gt;&lt;/code&gt; tag. When elements are grouped, the editor moves them together and disables the ability to edit them individually. After selecting one or more elements, you can create a new group using the context menu. You can likewise ungroup elements that are grouped together using the context menu.&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-grouping.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h4&gt;Copy, paste, remove, and re-order&lt;/h4&gt;
&lt;p&gt;The last core part of the editor is the ability to copy, paste, remove, and re-order elements. You can mark one or more elements and press copy to paste their content into your clipboard. Then you can click paste to insert a copy. If you had any item marked when you pasted, it would make sure to add the element just above that; otherwise, it will paste it in front of all items. You can likewise select any amount of items and press remove to remove them. The last function is under the menu called move, which gives options for moving elements back or forward in the render hierarchy.&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-copy-paste-remove.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h4&gt;Miscellaneous features&lt;/h4&gt;
&lt;p&gt;Some other features are also available in the editor but need more refinement. Among these are support for editing and creating linear gradients, editing and creating animations, and optimizing paths via the context menu and anchor movement interactions.&lt;/p&gt;
&lt;h3&gt;Customization&lt;/h3&gt;
&lt;p&gt;Now that we have seen what the editor can do out of the box let's see what we can do with it if we extend it. If you want to follow these steps yourself, you should start by following the &lt;a href="https://github.com/KristofferStrube/Blazor.SVGEditor#getting-started"&gt;Getting Started section in the repository README&lt;/a&gt;. If you don't want to follow these steps and just want to browse the nice videos, then that is also okay.&lt;/p&gt;
&lt;p&gt;The example customization we will make is a network editor consisting of nodes and connectors. Commonly known as a graph. To start, we create the following page to set up a minimal editor that doesn't support any elements, has no options for adding new elements, and has a subset of all our possible context menu features.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;&amp;lt;div style=&amp;quot;height:80vh&amp;quot;&amp;gt;
    &amp;lt;SVGEditor Input=@Input
               InputUpdated=&amp;quot;(string s) =&amp;gt; { Input = s; StateHasChanged(); }&amp;quot;¨
               SnapToInteger=true
               SupportedElements=SupportedElements
               AddNewSVGElementMenuItems=AddNewSVGElementMenuItems
               ActionMenuItems=ActionMenuItems /&amp;gt;
&amp;lt;/div&amp;gt;

@code {
    protected string Input = @&amp;quot;&amp;quot;;

    protected List&amp;lt;SupportedElement&amp;gt; SupportedElements { get; set; } = new()
    {
    };

    protected List&amp;lt;SupportedAddNewSVGElementMenuItem&amp;gt; AddNewSVGElementMenuItems { get; set; } = new()
    {
    };

    protected List&amp;lt;ActionMenuItem&amp;gt; ActionMenuItems { get; set; } = new() {
        new(typeof(StrokeMenuItem), (_, data) =&amp;gt; data is Shape shape &amp;amp;&amp;amp; !shape.IsChildElement),
        new(typeof(MoveMenuItem), (_, data) =&amp;gt; data is Shape shape &amp;amp;&amp;amp; !shape.IsChildElement),
        new(typeof(GroupMenuItem), (_, data) =&amp;gt; data is Shape shape &amp;amp;&amp;amp; !shape.IsChildElement),
        new(typeof(UngroupMenuItem), (_, data) =&amp;gt; data is G g &amp;amp;&amp;amp; !g.IsChildElement),
        new(typeof(RemoveMenuItem), (svgEditor, data) =&amp;gt; data is Shape &amp;amp;&amp;amp; !svgEditor.DisableRemoveElement),
        new(typeof(CopyMenuItem), (svgEditor, data) =&amp;gt; data is Shape &amp;amp;&amp;amp; !svgEditor.DisableCopyElement),
        new(typeof(PasteMenuItem), (svgEditor, _) =&amp;gt; !svgEditor.DisablePasteElement),
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Node.cs&lt;/h4&gt;
&lt;p&gt;We will first make the classes and components needed to control, render, and add new nodes. This will build on the existing code we have created for editing Circles. We first add a class called &lt;code&gt;Node&lt;/code&gt; that will handle how we interact with nodes and how we create new ones. We will later add more logic to this class to enrich its interaction with connectors.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class Node : Circle
{
    public Node(IElement element, SVGEditor svg) : base(element, svg)
    {
        string? id = element.GetAttribute(&amp;quot;id&amp;quot;);
        if (id is null || svg.Elements.Any(e =&amp;gt; e.Id == id))
        {
            Id = Guid.NewGuid().ToString();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The class extends &lt;code&gt;Circle&lt;/code&gt; as it shares most of its logic with it. The first thing we do in the constructor is to check if the SVG definition that the &lt;code&gt;Node&lt;/code&gt; is constructed from has a unique &lt;code&gt;Id&lt;/code&gt;, and if it doesn't, then assign a new &lt;code&gt;Id&lt;/code&gt; to it so that we have a way to reference each node individually.&lt;/p&gt;
&lt;p&gt;Next we override the &lt;code&gt;Presenter&lt;/code&gt; property to customize how we present the &lt;code&gt;Node&lt;/code&gt; with a &lt;code&gt;NodeEditor&lt;/code&gt; instead of using the inherited &lt;code&gt;CircleEditor&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public override Type Presenter =&amp;gt; typeof(NodeEditor);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also override the &lt;code&gt;R&lt;/code&gt; property that defines the radius of the &lt;code&gt;Circle&lt;/code&gt; so that it is always &lt;code&gt;50&lt;/code&gt; but still enables the &lt;code&gt;R&lt;/code&gt; to be set to something else.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public new double R { get =&amp;gt; 50; set =&amp;gt; base.R = value; }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we override the &lt;code&gt;Stroke&lt;/code&gt; property of the &lt;code&gt;Circle&lt;/code&gt; so that it also updates the &lt;code&gt;Fill&lt;/code&gt;. This aims to limit the user's options for editing the nodes to give a more homogeneous visual look.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public override string Stroke
{
    get =&amp;gt; base.Stroke;
    set
    {
        base.Stroke = value;
        int[] parts = value[1..].Chunk(2).Select(part =&amp;gt; int.Parse(part, System.Globalization.NumberStyles.HexNumber)).ToArray();
        Fill = &amp;quot;#&amp;quot; + string.Join(&amp;quot;&amp;quot;, parts.Select(part =&amp;gt; Math.Min(255, part + 50).ToString(&amp;quot;X2&amp;quot;)));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Our goal is to set the &lt;code&gt;Fill&lt;/code&gt; to a color that is a bit lighter than the newly set &lt;code&gt;Stroke&lt;/code&gt; color. We know the &lt;code&gt;Stroke&lt;/code&gt; will always be set using a 6-character long hex value prepended by a pound sign &lt;code&gt;'#'&lt;/code&gt;. To increase the color brightness, we chunk the 6 characters into chunks of 2 characters, each representing one of the color parts, i.e., red, green, and blue. We parse these chunks as hex values and use these parsed values to construct the &lt;code&gt;Fill&lt;/code&gt; color. To do this, we just join the parts back together as 2-digit hex values but increase the intensity of each color by 50, capped at 255 as that is the largest possible 2-digit hex value.&lt;/p&gt;
&lt;p&gt;The last part we add to the &lt;code&gt;Node&lt;/code&gt; class right now is a method for adding a new &lt;code&gt;Node&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static new void AddNew(SVGEditor SVG)
{
    IElement element = SVG.Document.CreateElement(&amp;quot;CIRCLE&amp;quot;);
    element.SetAttribute(&amp;quot;data-elementtype&amp;quot;, &amp;quot;node&amp;quot;);

    Node node = new(element, SVG)
    {
        Changed = SVG.UpdateInput,
        Stroke = &amp;quot;#28B6F6&amp;quot;,
        R = 50
    };

    (node.Cx, node.Cy) = SVG.LocalDetransform(SVG.LastRightClick);

    SVG.ClearSelectedShapes();
    SVG.SelectShape(node);
    SVG.AddElement(node);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first create the underlying XML element that the &lt;code&gt;Node&lt;/code&gt; will use as its data underlying data structure. We set the attribute &lt;code&gt;data-elementtype&lt;/code&gt; on this to &lt;code&gt;&amp;quot;node&amp;quot;&lt;/code&gt; so that we can distinguish it from other &lt;code&gt;&amp;lt;circle&amp;gt;&lt;/code&gt; tags.&lt;/p&gt;
&lt;p&gt;Then we construct a new &lt;code&gt;Node&lt;/code&gt; from this that will trigger the &lt;code&gt;SVGEditors&lt;/code&gt;´s &lt;code&gt;UpdateInput&lt;/code&gt; method whenever it changes and set its &lt;code&gt;Stroke&lt;/code&gt; color and radius to our defaults.&lt;/p&gt;
&lt;p&gt;We also set the center of the new &lt;code&gt;Node&lt;/code&gt; to be the last place that the user has right-clicked so that it will appear near where they last used the context menu. The &lt;code&gt;SVGEditor&lt;/code&gt; automatically keeps track of this position in the screen coordinate system. We might have panned around the SVG or zoomed in, so to get the original position in the coordinate system of the SVG, we parse the screen coordinate through the &lt;code&gt;SVGEditor&lt;/code&gt; method &lt;code&gt;LocalDetransform&lt;/code&gt; which can transform any point from the screen coordinate system to the SVG coordinate system.&lt;/p&gt;
&lt;p&gt;Lastly, we deselect all selected shapes, select the new &lt;code&gt;Node&lt;/code&gt;, and add it to the &lt;code&gt;SVGEditor&lt;/code&gt; using the &lt;code&gt;AddElement&lt;/code&gt; method.&lt;/p&gt;
&lt;h4&gt;NodeEditor.razor&lt;/h4&gt;
&lt;p&gt;We defined that the &lt;code&gt;Node&lt;/code&gt; should be presented by a &lt;code&gt;NodeEditor&lt;/code&gt; component. We want something very close to how we present a &lt;code&gt;Circle&lt;/code&gt;, but without the capability to edit the radius. That gives us the following component:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@using BlazorContextMenu
@using KristofferStrube.Blazor.SVGEditor.ShapeEditors
@using KristofferStrube.Blazor.SVGEditor.Extensions
@inherits ShapeEditor&amp;lt;Node&amp;gt;

&amp;lt;ContextMenuTrigger MenuId=&amp;quot;SVGMenu&amp;quot; WrapperTag=&amp;quot;g&amp;quot; Data=@SVGElement MouseButtonTrigger=&amp;quot;SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4&amp;quot;&amp;gt;
    &amp;lt;g transform=&amp;quot;translate(@SVGElement.SVG.Translate.x.AsString() @SVGElement.SVG.Translate.y.AsString()) scale(@SVGElement.SVG.Scale.AsString())&amp;quot;&amp;gt;
        &amp;lt;circle @ref=ElementReference
        @onfocusin=&amp;quot;FocusElement&amp;quot;
        @onfocusout=&amp;quot;UnfocusElement&amp;quot;
        @onpointerdown=&amp;quot;SelectAsync&amp;quot;
        @onkeyup=&amp;quot;KeyUp&amp;quot;
                tabindex=&amp;quot;@(SVGElement.IsChildElement ? -1 : 0)&amp;quot;
                cx=@SVGElement.Cx.AsString()
                cy=@SVGElement.Cy.AsString()
                r=@SVGElement.R.AsString()
                stroke=&amp;quot;@SVGElement.Stroke&amp;quot;
                stroke-width=&amp;quot;@SVGElement.StrokeWidth&amp;quot;
                stroke-linecap=&amp;quot;@SVGElement.StrokeLinecap.AsString()&amp;quot;
                stroke-linejoin=&amp;quot;@SVGElement.StrokeLinejoin.AsString()&amp;quot;
                stroke-dasharray=&amp;quot;@SVGElement.StrokeDasharray&amp;quot;
                stroke-dashoffset=&amp;quot;@SVGElement.StrokeDashoffset.AsString()&amp;quot;
                fill=&amp;quot;@SVGElement.Fill&amp;quot;
                style=&amp;quot;filter:brightness(@(SVGElement.Selected ? &amp;quot;0.8&amp;quot; : &amp;quot;1&amp;quot;))&amp;quot;&amp;gt;
        &amp;lt;/circle&amp;gt;
    &amp;lt;/g&amp;gt;
&amp;lt;/ContextMenuTrigger&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Another thing we have changed is that the circle tag applies a filter to darken its fill if selected. It extends the abstract &lt;code&gt;ShapeEditor&lt;/code&gt; component that implements all its necessary logic.&lt;/p&gt;
&lt;h4&gt;AddNewNodeMenuItem.razor&lt;/h4&gt;
&lt;p&gt;We also need to define a simple menu item that we can use to add new nodes.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@using BlazorContextMenu

&amp;lt;Item OnClick=&amp;quot;_ =&amp;gt; Node.AddNew(SVGEditor)&amp;quot;&amp;gt;
    &amp;lt;div class=&amp;quot;icon&amp;quot;&amp;gt;⚫&amp;lt;/div&amp;gt; New Node
&amp;lt;/Item&amp;gt;

@code {
    [CascadingParameter]
    public required SVGEditor SVGEditor { get; set; }

    [Parameter]
    public required object Data { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The component will get a reference to the &lt;code&gt;SVGEditor&lt;/code&gt; that it is used in through a &lt;code&gt;CascadingParameter&lt;/code&gt;. It also gets initialized with reference to the item that was last right-clicked through the &lt;code&gt;Data&lt;/code&gt; &lt;code&gt;Parameter&lt;/code&gt;, if there was any. We don't use the &lt;code&gt;Data&lt;/code&gt; property in our sample, but one way we could have used this would be to check if the last right-clicked element was a &lt;code&gt;Node&lt;/code&gt;, and if it were, then use the same &lt;code&gt;Stroke&lt;/code&gt; color. Instead, we simply invoke the &lt;code&gt;AddNew&lt;/code&gt; method when the menu item is clicked.&lt;/p&gt;
&lt;p&gt;Let's add these new components and classes to our sample razor page and see how it looks. We add the &lt;code&gt;Node&lt;/code&gt; class to our list of supported elements and specify that it should handle representing any tag that is a circle with the &lt;code&gt;data-elementtype&lt;/code&gt; set to &lt;code&gt;&amp;quot;node&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;protected List&amp;lt;SupportedElement&amp;gt; SupportedElements = new()
{
    new(typeof(Node), element =&amp;gt; element.TagName is &amp;quot;CIRCLE&amp;quot; &amp;amp;&amp;amp; element.GetAttribute(&amp;quot;data-elementtype&amp;quot;) == &amp;quot;node&amp;quot;),
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And we add the menu item for adding a new &lt;code&gt;Node&lt;/code&gt; to our list of menu items for the &amp;quot;Add New&amp;quot; sub-menu and specify that the menu item should always be presented.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;protected List&amp;lt;SupportedAddNewSVGElementMenuItem&amp;gt; AddNewSVGElementMenuItems = new()
{
    new(typeof(AddNewNodeMenuItem), (_,_) =&amp;gt; true),
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now when we run the project, we will be able to view, edit and create nodes.&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-just-nodes.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;h4&gt;Connector.cs&lt;/h4&gt;
&lt;p&gt;Now we are ready to add our connectors. We start of with creating a new class that extends the existing &lt;code&gt;Line&lt;/code&gt; class.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class Connector : Line
{
    public Connector(IElement element, SVGEditor svg) : base(element, svg)
    {
        UpdateLine();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When it is initialized, we call a method that will update the position of the line. We also define a custom presenter for the &lt;code&gt;Connector&lt;/code&gt; that slightly changes how it is presented and how a user can interact with it.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public override Type Presenter =&amp;gt; typeof(ConnectorEditor);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will not set the connector's position directly by moving its endpoints. Instead, this will be controlled by which nodes it is connected to. We define two properties for this that will simplify how we can access and update them later. We first define which &lt;code&gt;Node&lt;/code&gt; the connecter starts in.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public Node? From
{
    get
    {
        var from = (Node?)SVG.Elements.FirstOrDefault(e =&amp;gt; e is Node &amp;amp;&amp;amp; e.Id == Element.GetAttribute(&amp;quot;data-from&amp;quot;));
        _ = from?.RelatedConnectors.Add(this);
        return from;
    }
    set
    {
        if (From is { } from)
        {
            _ = from.RelatedConnectors.Remove(this);
        }
        if (value is null)
        {
            _ = Element.RemoveAttribute(&amp;quot;data-from&amp;quot;);
        }
        else
        {
            Element.SetAttribute(&amp;quot;data-from&amp;quot;, value.Id);
            _ = value.RelatedConnectors.Add(this);
        }
        Changed?.Invoke(this);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;When we get the &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt;, it finds the &lt;code&gt;Node&lt;/code&gt; among all registered elements that also match the &lt;code&gt;Id&lt;/code&gt; with the content of the &lt;code&gt;data-from&lt;/code&gt; attribute of the connecter. After this, it adds this connector to a &lt;code&gt;HashSet&lt;/code&gt; called &lt;code&gt;RelatedConnectors&lt;/code&gt; on the &lt;code&gt;Node&lt;/code&gt;.  If we set the &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt;, we remove the previous &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt; from the &lt;code&gt;HashSet&lt;/code&gt; and either remove the attribute if it was set to &lt;code&gt;null&lt;/code&gt; or set it to the &lt;code&gt;Id&lt;/code&gt; of the new &lt;code&gt;Node&lt;/code&gt; and again adds the connector to the &lt;code&gt;HashSet&lt;/code&gt;. We also invoke &lt;code&gt;Changed&lt;/code&gt; as we have changed some a part of the SVG definition when we set this property. We will show what we have added to the &lt;code&gt;Node&lt;/code&gt; class to support the &lt;code&gt;RelatedConnectors&lt;/code&gt; &lt;code&gt;HashSet&lt;/code&gt; in just a bit, but let's first show the code for the &lt;code&gt;To&lt;/code&gt; property as that is almost identical to the &lt;code&gt;From&lt;/code&gt; node.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public Node? To
{
    get
    {
        var to = (Node?)SVG.Elements.FirstOrDefault(e =&amp;gt; e is Node &amp;amp;&amp;amp; e.Id == Element.GetAttribute(&amp;quot;data-to&amp;quot;));
        _ = to?.RelatedConnectors.Add(this);
        return to;
    }
    set
    {
        if (To is { } to)
        {
            _ = to.RelatedConnectors.Remove(this);
        }
        if (value is null)
        {
            _ = Element.RemoveAttribute(&amp;quot;data-to&amp;quot;);
        }
        else
        {
            Element.SetAttribute(&amp;quot;data-to&amp;quot;, value.Id);
            _ = value.RelatedConnectors.Add(this);
        }
        Changed?.Invoke(this);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Let's see what we have added to the &lt;code&gt;Node&lt;/code&gt; class to support the relationship between the connectors and nodes.
We first add the &lt;code&gt;HashSet&lt;/code&gt; that we mentioned to the &lt;code&gt;Node&lt;/code&gt; class.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public HashSet&amp;lt;Connector&amp;gt; RelatedConnectors { get; } = new();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also override the &lt;code&gt;HandlePointerMove&lt;/code&gt; method that every &lt;code&gt;Shape&lt;/code&gt; implements to ensure that the related connectors are updated when the &lt;code&gt;Node&lt;/code&gt; is moved.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public override void HandlePointerMove(PointerEventArgs eventArgs)
{
    base.HandlePointerMove(eventArgs);
    if (SVG.EditMode is EditMode.Move)
    {
        foreach (Connector connector in RelatedConnectors)
        {
            connector.UpdateLine();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;All related connectors should also be removed if a &lt;code&gt;Node&lt;/code&gt; is removed. To support this, we can override the &lt;code&gt;BeforeBeingRemoved&lt;/code&gt; method, which is called before any &lt;code&gt;ISVGElement&lt;/code&gt; is removed from the editor.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public override void BeforeBeingRemoved()
{
    foreach (Connector connector in RelatedConnectors)
    {
        SVG.RemoveElement(connector);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next we will go back to the &lt;code&gt;Connector&lt;/code&gt; class. We start of with implementing the method for adding a new &lt;code&gt;Connector&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static void AddNew(SVGEditor SVG, Node from)
{
    IElement element = SVG.Document.CreateElement(&amp;quot;LINE&amp;quot;);
    element.SetAttribute(&amp;quot;data-elementtype&amp;quot;, &amp;quot;connector&amp;quot;);

    Connector connector = new(element, SVG)
    {
        Changed = SVG.UpdateInput,
        Stroke = &amp;quot;black&amp;quot;,
        StrokeWidth = &amp;quot;5&amp;quot;,
        From = from
    };
    SVG.EditMode = EditMode.Add;

    SVG.ClearSelectedShapes();
    SVG.SelectShape(connector);
    SVG.AddElement(connector);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We create a new XML element in the form of a line. We set the &lt;code&gt;data-elementtype&lt;/code&gt; to &lt;code&gt;&amp;quot;connector&amp;quot;&lt;/code&gt; on this, which we will use later to specify that it should only be the &lt;code&gt;Connector&lt;/code&gt; class that handles this element. Next, we create a new &lt;code&gt;Connector&lt;/code&gt; instance and set some defaults. This &lt;code&gt;AddNew&lt;/code&gt; method differs from the one we specified by the &lt;code&gt;Node&lt;/code&gt;. This method also takes a &lt;code&gt;Node&lt;/code&gt; as a parameter. We use this to select which &lt;code&gt;Node&lt;/code&gt; the &lt;code&gt;Connector&lt;/code&gt; should go from so that we can finish the addition of the &lt;code&gt;Connector&lt;/code&gt; with very few interactions. In the end, we do the same thing as for the &lt;code&gt;Node&lt;/code&gt; &lt;code&gt;AddNew&lt;/code&gt; method and clear all selected shapes, set the new &lt;code&gt;Connector&lt;/code&gt; as the single selected shape, and add the new &lt;code&gt;Connector&lt;/code&gt; to the &lt;code&gt;SVGEditor&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;To give the user some feedback while selecting a second &lt;code&gt;Node&lt;/code&gt; to connect to, we want to update where the &lt;code&gt;Connector&lt;/code&gt; points to follow the mouse until the user has decided on a &lt;code&gt;Node&lt;/code&gt;. To control this we again override the &lt;code&gt;HandlePointerMove&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public override void HandlePointerMove(PointerEventArgs eventArgs)
{
    if (SVG.EditMode is EditMode.Add)
    {
        (X2, Y2) = SVG.LocalDetransform((eventArgs.OffsetX, eventArgs.OffsetY));
        SetStart((X2, Y2));
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don't want the &lt;code&gt;Connector&lt;/code&gt; to handle a pointer move in any other case than when we are adding a new one. In this case, we set the end position equal to the pointer position, which is parsed to the method. After this, we update the position of the start position so that it is oriented towards where the cursor is currently. Let's see how we do that now. This includes a tiny bit of trigonometry, so be warned.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public void SetStart((double x, double y) towards)
{
    double differenceX = towards.x - From!.Cx;
    double differenceY = towards.y - From!.Cy;
    double distance = Math.Sqrt((differenceX * differenceX) + (differenceY * differenceY));

    if (distance &amp;gt; 0)
    {
        X1 = From!.Cx + (differenceX / distance * 50);
        Y1 = From!.Cy + (differenceY / distance * 50);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first calculate the difference between the point we are drawing toward and the center of the &lt;code&gt;Node&lt;/code&gt; we draw from for the &lt;code&gt;x&lt;/code&gt; and &lt;code&gt;y&lt;/code&gt; axes, respectively. Then we calculate the Euclidean distance between the two same points using the Pythagorean theorem. If the cursor is any distance from the &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt;, we update the start of the line. We wish to start the line at the edge of the &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt; circle and find the point closest to the cursor. We already have a vector that points to the cursor &lt;code&gt;(differenceX, differenceY)&lt;/code&gt;, so we start with normalizing it by dividing it by its length, which is our &lt;code&gt;distance&lt;/code&gt;, and then we multiply it with &lt;code&gt;50&lt;/code&gt; to make that vector point out to the edge of the &lt;code&gt;Node&lt;/code&gt; as that has a radius of &lt;code&gt;50&lt;/code&gt;. Then we add this re-scaled vector to the center of the &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Before continuing, let's demo this to see that it updates the line to point to the edge of the &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt;. To show this, I've added a &lt;code&gt;@ref&lt;/code&gt; to the &lt;code&gt;SVGEditor&lt;/code&gt; in our sample page, programmatically initialized a new &lt;code&gt;Node&lt;/code&gt;, and started the addition of a &lt;code&gt;Connector&lt;/code&gt; like just as a temporary change.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private SVGEditor SVGEditor { get; set; } = default!;

protected override void OnAfterRender(bool firstRender)
{
    if (!firstRender) return;

    Node.AddNew(SVGEditor);
    var node = (Node)SVGEditor.Elements.First();
    Connector.AddNew(SVGEditor, node);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-connector-going-from-node.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;Next, we need to be able to select a &lt;code&gt;Node&lt;/code&gt; while adding a &lt;code&gt;Connector&lt;/code&gt; to end the interaction. &lt;code&gt;Shape&lt;/code&gt;s cannot be selected when other elements are being created by default, so we first need to override this inherited logic of the &lt;code&gt;NodeEditor&lt;/code&gt;. We add the following to the code section of our &lt;code&gt;NodeEditor&lt;/code&gt; component:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public new async Task SelectAsync(MouseEventArgs eventArgs)
{
    if (SVGElement.SVG.EditMode is EditMode.Add &amp;amp;&amp;amp; SVGElement.SVG.SelectedShapes.Any(s =&amp;gt; s is Connector))
    {
        SVGElement.SVG.SelectedShapes.Add(SVGElement);
    }
    else
    {
        await base.SelectAsync(eventArgs);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We wish to change the behavior of the &lt;code&gt;SelectAsync&lt;/code&gt; method which is invoked when the pointer is pressed down on a &lt;code&gt;Node&lt;/code&gt;, so we &lt;code&gt;new&lt;/code&gt; the &lt;code&gt;SelectAsync&lt;/code&gt; method in the component. In this, we have two cases. If the &lt;code&gt;SVGEditor&lt;/code&gt; is in &lt;code&gt;Add&lt;/code&gt; mode and any &lt;code&gt;Connector&lt;/code&gt; is currently selected, then we add the &lt;code&gt;Node&lt;/code&gt; to the list of selected shapes. Else we call the base implementation of the &lt;code&gt;SelectAsync&lt;/code&gt; method.&lt;/p&gt;
&lt;p&gt;Now we can select a &lt;code&gt;Node&lt;/code&gt; while a &lt;code&gt;Connector&lt;/code&gt; is being added. Then we need to handle when the pointer is being raised while the &lt;code&gt;Connecter&lt;/code&gt; is added. We override the &lt;code&gt;HandlePointerUp&lt;/code&gt; on the &lt;code&gt;Connector&lt;/code&gt; class for this.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public override void HandlePointerUp(PointerEventArgs eventArgs)
{
    if (SVG.EditMode is EditMode.Add
        &amp;amp;&amp;amp; SVG.SelectedShapes.FirstOrDefault(s =&amp;gt; s is Node node &amp;amp;&amp;amp; node != From) is Node { } to)
    {
        if (to.RelatedConnectors.Any(c =&amp;gt; c.To == From || c.From == From))
        {
            Complete();
        }
        else
        {
            To = to;
            SVG.EditMode = EditMode.None;
            UpdateLine();
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We only care about the case where we are adding a &lt;code&gt;Connector&lt;/code&gt; so we first check for this and then check if we can find any selected shape that is a &lt;code&gt;Node&lt;/code&gt;. If we have matched this, we then do one of two things. If the two nodes that are going to be connected already are connected, then we call &lt;code&gt;Complete&lt;/code&gt;, which we will implement to remove this unfinished &lt;code&gt;Connector&lt;/code&gt; in a bit. We do this so that two nodes can't be connected more than once. If there wasn't already a &lt;code&gt;Connection&lt;/code&gt; present between these two nodes then we see the &lt;code&gt;To&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt;, reset the &lt;code&gt;SVGEditor&lt;/code&gt; mode, and update its placement.&lt;/p&gt;
&lt;p&gt;Before going to the &lt;code&gt;UpdateLine&lt;/code&gt; method, let's see the &lt;code&gt;Complete&lt;/code&gt; method we used.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public override void Complete()
{
    if (To is null)
    {
        SVG.RemoveElement(this);
        Changed?.Invoke(this);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method can also be called from the context menu to enable the user to cancel creating a new &lt;code&gt;Connector&lt;/code&gt; after starting. So in these two cases, we remove the temporary &lt;code&gt;Connector&lt;/code&gt; if &lt;code&gt;To&lt;/code&gt; is not set.&lt;/p&gt;
&lt;p&gt;Now let's see the &lt;code&gt;UpdateLine&lt;/code&gt; method that we have referenced a couple of times now.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public void UpdateLine()
{
    if (From is null || To is null)
    {
        (X1, Y1) = (X2, Y2);
        return;
    }

    double differenceX = To.Cx - From.Cx;
    double differenceY = To.Cy - From.Cy;
    double distance = Math.Sqrt((differenceX * differenceX) + (differenceY * differenceY));

    if (distance &amp;lt; 100)
    {
        (X1, Y1) = (X2, Y2);
    }
    else
    {
        SetStart((To.Cx, To.Cy));
        X2 = To.Cx - (differenceX / distance * 50);
        Y2 = To.Cy - (differenceY / distance * 50);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If the &lt;code&gt;From&lt;/code&gt; or &lt;code&gt;To&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt; is not set, we set the start of the line equal to the end position so that it will not appear.&lt;/p&gt;
&lt;p&gt;Then we do something very close to what we have seen previously by finding the vector from the &lt;code&gt;To&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt; to the &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt;. Then we calculate the length of this vector. And if the length of the vector is less than &lt;code&gt;100&lt;/code&gt;, then we know that they are so close that the &lt;code&gt;Connector&lt;/code&gt; should not be rendered, so we once again set the start equal to the end. Else we set the start position to point towards the &lt;code&gt;To&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt; and then calculate the point on the edge of the circle of the &lt;code&gt;To&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt; closest to the &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt; exactly like we did in the &lt;code&gt;SetStart&lt;/code&gt; method for the &lt;code&gt;From&lt;/code&gt; &lt;code&gt;Node&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;ConnectorEditor.razor&lt;/h4&gt;
&lt;p&gt;We also need to add the custom &lt;code&gt;ConnectorEditor&lt;/code&gt; we previously referenced in the &lt;code&gt;Connector&lt;/code&gt;. We have just copied the &lt;code&gt;LineEditor&lt;/code&gt; component and made some adjustments for this.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@using BlazorContextMenu
@using KristofferStrube.Blazor.SVGEditor.Extensions;
@using KristofferStrube.Blazor.SVGEditor.ShapeEditors;
@inherits ShapeEditor&amp;lt;Connector&amp;gt;

&amp;lt;ContextMenuTrigger MenuId=&amp;quot;SVGMenu&amp;quot; WrapperTag=&amp;quot;g&amp;quot; Data=@SVGElement MouseButtonTrigger=&amp;quot;SVGElement.ShouldTriggerContextMenu ? MouseButtonTrigger.Right : (MouseButtonTrigger)4&amp;quot;&amp;gt;
    &amp;lt;g style=&amp;quot;@(SVGElement.SVG.EditMode is EditMode.Add ? &amp;quot;pointer-events:none;&amp;quot; : &amp;quot;&amp;quot;)&amp;quot; transform=&amp;quot;translate(@SVGElement.SVG.Translate.x.AsString() @SVGElement.SVG.Translate.y.AsString()) scale(@SVGElement.SVG.Scale.AsString())&amp;quot;&amp;gt;
        &amp;lt;line @ref=ElementReference
        @onfocusin=&amp;quot;FocusElement&amp;quot;
        @onfocusout=&amp;quot;UnfocusElement&amp;quot;
        @onpointerdown=&amp;quot;SelectAsync&amp;quot;
        @onkeyup=&amp;quot;KeyUp&amp;quot;
              tabindex=&amp;quot;@(SVGElement.IsChildElement ? -1 : 0)&amp;quot;
              x1=@SVGElement.X1.AsString()
              y1=@SVGElement.Y1.AsString()
              x2=@SVGElement.X2.AsString()
              y2=@SVGElement.Y2.AsString()
              stroke=&amp;quot;@SVGElement.Stroke&amp;quot;
              stroke-width=&amp;quot;@SVGElement.StrokeWidth&amp;quot;
              stroke-linecap=&amp;quot;@SVGElement.StrokeLinecap.AsString()&amp;quot;
              stroke-linejoin=&amp;quot;@SVGElement.StrokeLinejoin.AsString()&amp;quot;
              stroke-dasharray=&amp;quot;@SVGElement.StrokeDasharray&amp;quot;
              stroke-dashoffset=&amp;quot;@SVGElement.StrokeDashoffset.AsString()&amp;quot;
              fill=&amp;quot;@SVGElement.Fill&amp;quot;&amp;gt;
        &amp;lt;/line&amp;gt;
    &amp;lt;/g&amp;gt;
&amp;lt;/ContextMenuTrigger&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have changed two things for the markup part of the &lt;code&gt;ConnectorEditor&lt;/code&gt;. We have added a &lt;code&gt;style&lt;/code&gt; tag to the &lt;code&gt;&amp;lt;g&amp;gt;&lt;/code&gt; that encloses the &lt;code&gt;&amp;lt;line&amp;gt;&lt;/code&gt; itself. The style tag makes it so that it will not handle any pointer events if the editor is currently in the &lt;code&gt;Add&lt;/code&gt; mode. This is to ensure that we can click through the line when we are adding a new &lt;code&gt;Connector&lt;/code&gt; so that it doesn't block any &lt;code&gt;Node&lt;/code&gt; we would like to click. Secondly, we have removed the two anchors that normally appear when a &lt;code&gt;Line&lt;/code&gt; is selected to make the UI less noisy. We have also overridden the &lt;code&gt;OnAfterRenderAsync&lt;/code&gt; method in the code section of the component.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;protected override async Task OnAfterRenderAsync(bool firstRender)
{
    if (firstRender)
    {
        SVGElement.UpdateLine();
    }
    await base.OnAfterRenderAsync(firstRender);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This ensures that all connectors get their position updates when first rendered.&lt;/p&gt;
&lt;h4&gt;AddNewConnectorMenuItem.razor&lt;/h4&gt;
&lt;p&gt;The last component we need is the context menu item for when we want to add a new &lt;code&gt;Connector&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@using BlazorContextMenu

@if (Data is Node node)
{
    &amp;lt;Item OnClick=&amp;quot;_ =&amp;gt; Connector.AddNew(SVGEditor, node)&amp;quot;&amp;gt;
        &amp;lt;div class=&amp;quot;icon&amp;quot;&amp;gt;〰&amp;lt;/div&amp;gt; New Connector
    &amp;lt;/Item&amp;gt;
}

@code {
    [CascadingParameter]
    public required SVGEditor SVGEditor { get; set; }

    [Parameter]
    public required object Data { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This menu item is a bit more complex than the one we made for adding a &lt;code&gt;Node&lt;/code&gt; as we only want it to render if we opened the context menu from a &lt;code&gt;Node&lt;/code&gt;. So we check the &lt;code&gt;Data&lt;/code&gt; property to get the &lt;code&gt;Node&lt;/code&gt; that was right-clicked if there was any, and parse that to the &lt;code&gt;AddNew&lt;/code&gt; method if the menu item is clicked.&lt;/p&gt;
&lt;p&gt;Lastly we need to update the configuration in our sample page to include these new handlers and components. We first add the &lt;code&gt;Connector&lt;/code&gt; class to the list of &lt;code&gt;SupportedElements&lt;/code&gt; and specify that it can handle any &lt;code&gt;&amp;lt;line&amp;gt;&lt;/code&gt; tag with a &lt;code&gt;data-elementtype&lt;/code&gt; attribute equal to &lt;code&gt;&amp;quot;connector&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;protected List&amp;lt;SupportedElement&amp;gt; SupportedElements { get; set; } = new()
{
    new(typeof(Node), element =&amp;gt; element.TagName is &amp;quot;CIRCLE&amp;quot; &amp;amp;&amp;amp; element.GetAttribute(&amp;quot;data-elementtype&amp;quot;) == &amp;quot;node&amp;quot;),
    new(typeof(Connector), element =&amp;gt; element.TagName is &amp;quot;LINE&amp;quot; &amp;amp;&amp;amp; element.GetAttribute(&amp;quot;data-elementtype&amp;quot;) == &amp;quot;connector&amp;quot;),
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then, we add the menu item for adding new connectors and define that it should only count as a menu item if the context menu started from a &lt;code&gt;Node&lt;/code&gt; as we defined in the component itself.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;protected List&amp;lt;SupportedAddNewSVGElementMenuItem&amp;gt; AddNewSVGElementMenuItems { get; set; } = new()
{
    new(typeof(AddNewNodeMenuItem), (_,_) =&amp;gt; true),
    new(typeof(AddNewConnectorMenuItem), (_,data) =&amp;gt; data is Node)
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now let's get to the final demo with all the parts working together.&lt;/p&gt;
&lt;p&gt;&lt;video width="600px" src="https://kristoffer-strube.dk/videos/svg-editor-graph-editor.mp4" autoplay muted controls loop&gt;&lt;/video&gt;&lt;/p&gt;
&lt;p&gt;You might have noticed that the area select tool also had a new orange color in this sample video. We changed this by adding the following scoped CSS styling to our sample page.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-css"&gt;::deep .anchor-primary {
    stroke: orange;
}

::deep .box {
    fill: orange;
    fill-opacity: 0.5;
    stroke: orange;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can try the demo shown in the video yourself here: &lt;a href="https://kristofferstrube.github.io/Blazor.SVGEditor/CustomElements/"&gt;kristofferstrube.github.io/Blazor.SVGEditor/CustomElements&lt;/a&gt;
And you can see the source code for the whole sample project, including some other demo pages, here: &lt;a href="https://github.com/KristofferStrube/Blazor.SVGEditor/tree/120101d2fce7f0f561697045f82133501836d823/samples/KristofferStrube.Blazor.SVGEditor.WasmExample"&gt;Demo project source code&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Future work&lt;/h3&gt;
&lt;p&gt;I will continue to develop on the project whenever I find some feature or nice interaction that I would like to add. I currently have three open issues if you would to contribute yourself: &lt;a href="https://github.com/KristofferStrube/Blazor.SVGEditor/issues/5" target="_blank"&gt;#5&lt;/a&gt;, &lt;a href="https://github.com/KristofferStrube/Blazor.SVGEditor/issues/11" target="_blank"&gt;#11&lt;/a&gt;, and  &lt;a href="https://github.com/KristofferStrube/Blazor.SVGEditor/issues/12" target="_blank"&gt;#12&lt;/a&gt;. One project that I plan to use the package in already is a demo for my &lt;a href="https://github.com/KristofferStrube/Blazor.WebAudio"&gt;Web Audio Blazor wrapper&lt;/a&gt;. The demo will be a network of sound sources, processors, and outputs that can be wired together to make some simple audio editing from the browser using Blazor.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;At the start of the article, we went through all the different features of the Blazor.SVGEditor package. Features include parsing SVG definitions, adding, selecting, editing, moving, removing shapes, and navigating the canvas using the mouse. Then we went through a sample project where we added some custom handlers for SVG elements to the editor and made a network editor with these. While developing this customization, we also saw how we could still use many of the features in the package by inheriting from existing shapes. If you have any feedback or questions relating to the article, feel free to contact me on any of my social media profiles.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">typed-exceptions-for-jsinterop-in-blazor</guid><link>https://kristoffer-strube.dk/post/typed-exceptions-for-jsinterop-in-blazor/</link><title>Typed exceptions for JSInterop in Blazor</title><description>When developing applications in Blazor we can invoke JavaScript function from C# by using JSInterop. Many types of errors can be thrown when calling these methods. Especially if something unexpected happens like missing permissions or if one of the arguments is invalid. The errors thrown from JS get bubbled up to our method invocation in C# and we can catch these JSExceptions using a try-catch statement. The exception contain the message of the original error, but we lose the context of which error type this was which has led people to do a lot of custom error handling that instead looks for key phrases or words in the error message. In this article, we look at how the existing error-handling implementation is made in Blazor. We then look at how we can map the error types defined in the standard WebIDL specification to C# exceptions, and in the end, we show some examples of how to use this in practice when making JS invocations.</description><pubDate>Fri, 26 May 2023 00:00:00 +0200</pubDate><a10:updated>2023-05-26T00:00:00+02:00</a10:updated><a10:content type="text">&lt;h3&gt;Classic example of handling exceptions from JSInterop&lt;/h3&gt;
&lt;p&gt;With the current way that Blazor propagates errors to us, we are often left with some rather simple ways of finding out what actually happened. Let's see what happens when we try to call some JS that will fail. The example we will work with is invoking a method that does not exist but which we expect might throw a JS &lt;code&gt;AbortError&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@inject IJSRuntime JSRuntime

@code {
    protected override async Task OnInitializedAsync()
    {
        await JSRuntime.InvokeVoidAsync(&amp;quot;nonExistingMethod&amp;quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will see the following error in the console of our browser.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;crit: Microsoft.AspNetCore.Components.WebAssembly.Rendering.WebAssemblyRenderer[100]
      Unhandled exception rendering component: Could not find 'nonExistingMethod' ('nonExistingMethod' was undefined).
      Error: Could not find 'nonExistingMethod' ('nonExistingMethod' was undefined).
          at http://localhost:5278/_framework/blazor.webassembly.js:1:328
          at Array.forEach (&amp;lt;anonymous&amp;gt;)
          at a.findFunction (http://localhost:5278/_framework/blazor.webassembly.js:1:296)
          ... This continues with a very long stack trace that is not relevant to the exception. Not until this part which is from the .NET call stack:
   at Microsoft.JSInterop.JSRuntime.&amp;lt;InvokeAsync&amp;gt;d__16`1[[Microsoft.JSInterop.Infrastructure.IJSVoidResult, Microsoft.JSInterop, Version=7.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60]].MoveNext()
   at Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync(IJSRuntime jsRuntime, String identifier, Object[] args)
   at test_project.Pages.Index.OnInitializedAsync() in F:\repos\test-project\Pages\Index.razor:line 8
   at Microsoft.AspNetCore.Components.ComponentBase.RunInitAndSetParametersAsync()
   at Microsoft.AspNetCore.Components.RenderTree.Renderer.GetErrorHandledTask(Task taskToHandle, ComponentState owningComponentState)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And if we try to capture the exception and read its &lt;code&gt;Message&lt;/code&gt; we still get the same exception except we don't get the .NET call stack.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@inject IJSRuntime JSRuntime

&amp;lt;pre&amp;gt;&amp;lt;code&amp;gt;
@exceptionMessage
&amp;lt;/code&amp;gt;&amp;lt;/pre&amp;gt;

@code {
    string? exceptionMessage;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            await JSRuntime.InvokeVoidAsync(&amp;quot;nonExistingMethod&amp;quot;);
        }
        catch (JSException jsex)
        {
            exceptionMessage = jsex.Message;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And this message isn't really a nice user-facing error to get. So what we have done earlier is either to forget entirely what the exception said and just write something like &lt;strong&gt;&amp;quot;Something bad happened!&amp;quot;&lt;/strong&gt; which doesn't help anyone. Or the better case which is to check for matching parts of the exception message. That could look something like this if we expect that JS could either throw an &lt;code&gt;AbortError&lt;/code&gt; or some other unexpected exception.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@inject IJSRuntime JSRuntime
@inject ILogger Logger

&amp;lt;span style=&amp;quot;color: red;&amp;quot;&amp;gt;@exceptionMessage&amp;lt;/span&amp;gt;

@code {
    string? exceptionMessage;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            await JSRuntime.InvokeVoidAsync(&amp;quot;nonExistingMethod&amp;quot;);
        }
        catch (JSException jsex)
        {
            if (jsex.Message.Contains(&amp;quot;is aborted&amp;quot;))
            {
                exceptionMessage = &amp;quot;The execution of the method was canceled.&amp;quot;;
            }
            else
            {
                exceptionMessage = &amp;quot;An unexpected error happened.&amp;quot;;
                Logger.LogError(exceptionMessage, jsex);
            }
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is a bit hand-wavy. We first need to know that the exception message contained the words &lt;code&gt;&amp;quot;is aborted&amp;quot;&lt;/code&gt; and we then have to hope that no other exception could happen that would also contain those words which would make for a false positive.&lt;/p&gt;
&lt;h3&gt;How Blazor currently handles errors in JSInterop&lt;/h3&gt;
&lt;p&gt;It can be tough to find out why an error happens when an invocation is not successful as we saw above. The root of the problem is that we don't get all information about why an error happens when Blazor handles an error. Most noticeably we don't get information about the &lt;code&gt;name&lt;/code&gt; of the specific type of error. To find out why let's look at the TypeScript code that handles Blazors dynamic invocation of functions in the &lt;a href="https://github.com/dotnet/aspnetcore/blob/main/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L401-L430"&gt;beginInvokeJSFromDotNet function in Microsoft.JSInterop.ts&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-ts"&gt;beginInvokeJSFromDotNet: (asyncHandle: number, identifier: string, argsJson: string, resultType: JSCallResultType, targetInstanceId: number): void =&amp;gt; {
    // Coerce synchronous functions into async ones, plus treat
    // synchronous exceptions the same as async ones
    const promise = new Promise&amp;lt;any&amp;gt;(resolve =&amp;gt; {
        const synchronousResultOrPromise = findJSFunction(identifier, targetInstanceId).apply(null, parseJsonWithRevivers(argsJson));
        resolve(synchronousResultOrPromise);
    });

    // We only listen for a result if the caller wants to be notified about it
    if (asyncHandle) {
        // On completion, dispatch result back to .NET
        // Not using &amp;quot;await&amp;quot; because it codegens a lot of boilerplate
        promise
        .then(result =&amp;gt; stringifyArgs([asyncHandle, true, createJSCallResult(result, resultType)]))
        .then(
            result =&amp;gt; getRequiredDispatcher().endInvokeJSFromDotNet(asyncHandle, true, result),
            error =&amp;gt; getRequiredDispatcher().endInvokeJSFromDotNet(asyncHandle, false, JSON.stringify([
                asyncHandle,
                false,
                formatError(error)
            ]))
        );
    }
},
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;They start off by making any potential synchronous method asynchronous by wrapping it in a promise. In the promise, they follow the identifier path to the function that should be invoked and then apply the arguments to that function using the &lt;code&gt;apply&lt;/code&gt; method. This is all good and basically just means that they have fewer places in the code that varies. Next, they resolve the newly created promise and handle the two outcomes of the function. Either it was successful or it errored. In either case, we call back to .NET with some payload and a flag indicating if the invocation was successful. If it was successful we simply serialize the result and return that. But when it errors we instead call and return the result of the method &lt;a href="https://github.com/dotnet/aspnetcore/blob/main/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L540-L546"&gt;formatError function in Microsoft.JSInterop.ts&lt;/a&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-ts"&gt;function formatError(error: Error | string): string {
    if (error instanceof Error) {
        return `${error.message}\n${error.stack}`;
    }

    return error ? error.toString() : &amp;quot;null&amp;quot;;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This is what formats the error in the well-known format we see in the browser console or the &lt;code&gt;JSException&lt;/code&gt;'s &lt;code&gt;Message&lt;/code&gt; when making JSInteorp calls. Do you see what we are missing? Yes exactly, the &lt;code&gt;name&lt;/code&gt; of the specific &lt;code&gt;Error&lt;/code&gt;. We have lost the type of the original error. So that is exactly what we want to get back so that we can handle the original grouping of errors instead of resorting to matching on parts of the message.&lt;/p&gt;
&lt;h3&gt;Mapping JS errors to C# exceptions&lt;/h3&gt;
&lt;p&gt;We want to do something similar to what the standard JSInterop has already done but include some other information. Sadly we can't change or use the existing code for the JSInterop functionality as most of it is internal. So instead we need to wrap the existing implementations and work around some of the mechanisms. But before we go there let's look at what different kinds of errors there are in JS.&lt;/p&gt;
&lt;h4&gt;Overview of JS error types&lt;/h4&gt;
&lt;p&gt;There are 6 types of errors that we should be concerned with when working within the browser.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;EvalError&lt;/li&gt;
&lt;li&gt;RangeError&lt;/li&gt;
&lt;li&gt;ReferenceError&lt;/li&gt;
&lt;li&gt;SyntaxError&lt;/li&gt;
&lt;li&gt;TypeError&lt;/li&gt;
&lt;li&gt;URIError&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Each of these is used in a different scenario and can also be referred to as &lt;em&gt;Native Error Types&lt;/em&gt;.
Apart from these we also have some standard exceptions called the &lt;code&gt;DOMException&lt;/code&gt;s. They are generally used for errors that happens as an effect of the user, the browser, or the system doing something unexpected.
&lt;code&gt;DOMException&lt;/code&gt;s are also errors, so we will simply refer to both JS errors and JS exceptions as errors from now on. There are many different kinds of &lt;code&gt;DOMExceptions&lt;/code&gt; as well that are defined in the &lt;a href="https://webidl.spec.whatwg.org/#idl-DOMException-error-names"&gt;WebIDL standard specifications&lt;/a&gt;. They differ only by having different names and then of cause by being used in different scenarios. There are &lt;code&gt;29&lt;/code&gt; predefined error names that are currently valid, so let's not list them all. The ones you might already know could be the &lt;code&gt;NotFoundError&lt;/code&gt;, the &lt;code&gt;NotSupportedError&lt;/code&gt;, and of cause the &lt;code&gt;AbortError&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;Capturing and rethrowing in JavaScript&lt;/h4&gt;
&lt;p&gt;The first step after having made clear that we are interested in the name of the exceptions is to somehow get this information into the exception that gets thrown at us. To do this we need to catch all exceptions on the JS side and rethrow them back for the Blazor implementation to handle. We could do this in all places where we need to invoke a method, but that would mean that we would need custom JS wrapper methods for &lt;strong&gt;ALL&lt;/strong&gt; JS invocations we wanted to make. Instead, let's make a simple method that can execute any general method similar to how the Blazor implementation does it, but then throw another exception. We make a new JS module (a JS file with exported functions) and start with the following function which we will import and call later.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;export async function callAsyncGlobalMethod(identifier, args) {
    return await callAsyncInstanceMethod(window, identifier, args);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method has two parameters. The first is the identifiers for the method that we will invoke and the second is the arguments that we will apply to this method. It then simply calls another method that we will define now and parses the &lt;code&gt;window&lt;/code&gt; object to this method. The &lt;code&gt;window&lt;/code&gt; object is the topmost context for the browser and thereby exposes most methods that you can otherwise invoke. So you could call &lt;code&gt;alert(&amp;quot;Hello!&amp;quot;)&lt;/code&gt; or you could call &lt;code&gt;window.alert(&amp;quot;Hello!&amp;quot;)&lt;/code&gt; and get the same result.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;export async function callAsyncInstanceMethod(instance, identifier, args) {
    try {
        var [functionObject, functionInstance] = resolveFunction(instance, identifier);
        return await functionInstance.apply(functionObject, args);
    }
    catch (error) {
        throw new DOMException(formatError(error), &amp;quot;AbortError&amp;quot;);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This function starts off by creating a &lt;code&gt;try-catch&lt;/code&gt; statement. In the &lt;code&gt;try&lt;/code&gt; block we call a function that resolves the reference to our function. Then it actually applies the arguments to the function, awaits it, and returns that. If it throws an error while resolving or awaiting the function we hit the &lt;code&gt;catch&lt;/code&gt; block. We capture the &lt;code&gt;error&lt;/code&gt; in the &lt;code&gt;catch&lt;/code&gt; block and throw a new error. The new error is a &lt;code&gt;DOMException&lt;/code&gt; constructed with the message given from calling &lt;code&gt;formatError&lt;/code&gt; with the &lt;code&gt;error&lt;/code&gt; and the &lt;code&gt;name&lt;/code&gt; set to &lt;code&gt;&amp;quot;AbortError&amp;quot;&lt;/code&gt;. It actually doesn't matter what type of error we throw as we already know that the Blazor code will completely ignore the &lt;code&gt;name&lt;/code&gt;, but we chose an &lt;code&gt;AbortError&lt;/code&gt; as that fits what we have done. Now let's first look at the &lt;code&gt;resolveFunction&lt;/code&gt; function that we used to get the function instance and the function object.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;function resolveFunction(instance, identifier)
{
    let identifierParts = identifier.split(&amp;quot;.&amp;quot;);
    var functionObject = instance;
    var functionInstance = instance[identifierParts[0]];
    for (let i = 1; i &amp;lt; identifierParts.length; i++) {
        if (functionInstance == undefined) {
            throw new ReferenceError(`Cannot read properties of undefined (reading '${identifierParts[i - 1]}').`);
        }
        functionObject = functionInstance;
        functionInstance = functionInstance[identifierParts[i]];
    }
    if (!(functionInstance instanceof Function)) {
        throw new TypeError(`'${identifierParts.slice(-1)}' is not a function.`);
    }
    return [functionObject, functionInstance];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;resolveFunction&lt;/code&gt; first splits the identifier into its parts. This is to support when calling methods on attributes like &lt;code&gt;window.caches.open&lt;/code&gt; with a single invocation. We then set that the object that the function should be called on (&lt;code&gt;functionObject&lt;/code&gt;) is the &lt;code&gt;instance&lt;/code&gt; parsed as a start and that the function itself is the property with the name matching the first part of the identifier. Then we go through all consecutive identifier parts &lt;em&gt;(if there are any left)&lt;/em&gt; and update the &lt;code&gt;functionObject&lt;/code&gt; to be the &lt;code&gt;functionInstance&lt;/code&gt; of the previous iteration, as it was actually an attribute, and once again set the &lt;code&gt;functionInstance&lt;/code&gt; to the next part of the identifier. Finally, we return the object that the function should be called on and the function itself. We do this as functions in JS always have to be called in some context.&lt;/p&gt;
&lt;p&gt;If an error were to happen when invoking the returned function or while resolving it then we format that error and re-throw it. Below we see the function we use to format the error. It is a rather simple function that simply returns a stringified object containing the error &lt;code&gt;name&lt;/code&gt;, &lt;code&gt;message&lt;/code&gt;, and &lt;code&gt;stack&lt;/code&gt;. This is similar to what Blazor does, but we use JSON as our structure instead of concatenating the details and we also include the &lt;code&gt;name&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;function formatError(error) {
    var name = error.name;
    if (error instanceof DOMException &amp;amp;&amp;amp; name == &amp;quot;SyntaxError&amp;quot;) {
        name = &amp;quot;DOMExceptionSyntaxError&amp;quot;;
    }
    return JSON.stringify({
        name: error.name,
        message: error.message,
        stack: error.stack
    })
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The one special thing we do is check if it is a &lt;code&gt;DOMException&lt;/code&gt; and if the &lt;code&gt;name&lt;/code&gt; is &lt;code&gt;&amp;quot;SyntaxError&amp;quot;&lt;/code&gt;. In that case we prepend the name with &lt;code&gt;&amp;quot;DOMException&amp;quot;&lt;/code&gt; as to disambiguate it from the &lt;code&gt;SyntaxError&lt;/code&gt; error type that also has this name. This is important as they have different purposes despite sharing a name.
And now the JS module is done and ready to be imported from C#.&lt;/p&gt;
&lt;h4&gt;Capturing and rethrowing in C#&lt;/h4&gt;
&lt;p&gt;The C# part should be a reusable class that follows the same pattern as the existing &lt;code&gt;IJSRuntime&lt;/code&gt; so that it will be easy to use instead. And how lucky can we be? &lt;code&gt;IJSRuntime&lt;/code&gt; is just an interface that we can implement. We create a new class called &lt;code&gt;ErrorHandlingJSRuntime&lt;/code&gt; that implements &lt;code&gt;IJSRuntime&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class ErrorHandlingJSRuntime : IJSRuntime
{
    public ValueTask&amp;lt;TValue&amp;gt; InvokeAsync&amp;lt;[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] TValue&amp;gt;(string identifier, object?[]? args)
    {
        throw new NotImplementedException();
    }

    public ValueTask&amp;lt;TValue&amp;gt; InvokeAsync&amp;lt;[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] TValue&amp;gt;(string identifier, CancellationToken cancellationToken, object?[]? args)
    {
        throw new NotImplementedException();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we implement the methods let's create a constructor that sets up the members we will need to make invocations.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;    private Lazy&amp;lt;Task&amp;lt;IJSObjectReference&amp;gt;&amp;gt; helperTask;

    public ErrorHandlingJSRuntime(IJSRuntime jSRuntime)
    {
        helperTask = new(async () =&amp;gt;
            await jSRuntime.InvokeAsync&amp;lt;IJSObjectReference&amp;gt;(&amp;quot;import&amp;quot;, &amp;quot;./_content/KristofferStrube.Blazor.WebIDL/KristofferStrube.Blazor.WebIDL.js&amp;quot;)
        );
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We take an &lt;code&gt;IJSRuntime&lt;/code&gt; in the constructor and create a new lazily loaded &lt;code&gt;IJSObjectReference&lt;/code&gt; helper task using it. The helper is created once needed by importing the module we made in the previous section. Now we are ready to implement the methods. The two methods are very similar but differ by having an extra &lt;code&gt;CancellationToken&lt;/code&gt; argument that can be used to cancel an invocation. An example of a time you would cancel an invocation is if the user navigates away from the page before the invocation is done. This makes the first method easy to implement as we can simply call the other method with the default value for the &lt;code&gt;CancellationToken&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public ValueTask&amp;lt;TValue&amp;gt; InvokeAsync&amp;lt;[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] TValue&amp;gt;(string identifier, params object?[]? args)
{
    return InvokeAsync&amp;lt;TValue&amp;gt;(identifier, CancellationToken.None, args);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You might have questioned the rather long signature for the methods by now. Especially the very long &lt;code&gt;DynamicallyAccessedMembers&lt;/code&gt; attribute added to the generic type parameter. These are used to tell that there will be used reflection on the types passed to the method. This is relevant in cases where you use AOT compilation as that will normally not allow reflection, but this makes it clear to the compiler that there will be used reflection on these types. It needs reflection since &lt;code&gt;System.Text.Json&lt;/code&gt; goes through all properties and constructors when deserializing.&lt;/p&gt;
&lt;p&gt;But now to the method that makes the invocation.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async ValueTask&amp;lt;TValue&amp;gt; InvokeAsync&amp;lt;[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors | DynamicallyAccessedMemberTypes.PublicFields | DynamicallyAccessedMemberTypes.PublicProperties)] TValue&amp;gt;(string identifier, CancellationToken cancellationToken, params object?[]? args)
{
    var helper = await helperTask.Value;
    try
    {
        return await helper.InvokeAsync&amp;lt;TValue&amp;gt;(&amp;quot;callAsyncGlobalMethod&amp;quot;, cancellationToken, identifier, args);
    }
    catch (JSException exception)
    {
        if (UnpackMessageOfExeption(exception) is not Error { } error)
        {
            throw;
        }
        throw MapToWebIDLException(error, exception);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first resolve our lazy-loaded helper module and then we start a &lt;code&gt;try-catch&lt;/code&gt; statement. In the &lt;code&gt;try&lt;/code&gt; block we invoke the &lt;code&gt;callAsyncGlobalMethod&lt;/code&gt; method from our helper module and parse in the &lt;code&gt;CancellationToken&lt;/code&gt; that might be the default &lt;code&gt;None&lt;/code&gt; value and the original &lt;code&gt;identifier&lt;/code&gt; and &lt;code&gt;args&lt;/code&gt; arguments as the &lt;code&gt;args&lt;/code&gt; parameter for the &lt;code&gt;InvokeAsync&lt;/code&gt; method on our helper module. If it is successful then that will be it and we just return the value. But if it fails then we hit the &lt;code&gt;catch&lt;/code&gt; block. We only catch &lt;code&gt;JSException&lt;/code&gt; as we don't want to handle any other errors like JSON serialization errors or the like. In the block, we call the method &lt;code&gt;UnpackMessageOfExeption&lt;/code&gt; that has the task of deserializing the &lt;code&gt;Message&lt;/code&gt; we packed in the JS function &lt;code&gt;formatError&lt;/code&gt;. If it could not unpack it then we throw the original exception as it clearly was not an exception that we had formatted. If we could unpack it then we map the &lt;code&gt;Error&lt;/code&gt; and the &lt;code&gt;JSException&lt;/code&gt; to some other &lt;code&gt;Exception&lt;/code&gt; type and throw that instead. Let's first implement the &lt;code&gt;UnpackMessageOfExeption&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;internal Error? UnpackMessageOfExeption(JSException exception)
{
    return JsonSerializer.Deserialize&amp;lt;Error?&amp;gt;(exception.Message[..^9].Trim());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first trim the last &lt;code&gt;9&lt;/code&gt; characters which are &lt;code&gt;&amp;quot;undefined&amp;quot;&lt;/code&gt; as this is the &lt;code&gt;stack&lt;/code&gt; that Blazor postfixes the &lt;code&gt;message&lt;/code&gt; with (because we threw the exception) and then we call &lt;code&gt;Trim&lt;/code&gt; to remove the line break. Then we use &lt;code&gt;System.Text.Json&lt;/code&gt; to deserialize the payload as an &lt;code&gt;Error?&lt;/code&gt; and return that. Pretty straightforward. Now to the &lt;code&gt;MapToWebIDLException&lt;/code&gt; method that maps this &lt;code&gt;Error&lt;/code&gt; to a new &lt;code&gt;Exception&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;internal WebIDLException MapToWebIDLException(Error error, JSException exception)
{
    if (ErrorMapper.TryGetValue(error.name, out Func&amp;lt;string, string, string?, Exception, WebIDLException&amp;gt;? creator))
    {
        return creator(error.name, error.message, error.stack, exception);
    }
    else
    {
        return new WebIDLException($&amp;quot;{error.name}: \&amp;quot;{error.message}\&amp;quot;&amp;quot;, null, exception);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method checks inside a dictionary for a key equal to the error name. The type of the dictionary is &lt;code&gt;Dictionary&amp;lt;string, Func&amp;lt;string, string, string?, Exception, WebIDLException&amp;gt;&amp;gt;&lt;/code&gt; but we will get back to that later. The value returned from the dictionary is a &lt;code&gt;Func&lt;/code&gt; that takes 3 strings and an &lt;code&gt;Exception&lt;/code&gt; and parses back a &lt;code&gt;WebIDLException&lt;/code&gt;. If we successfully find a key that matches then we invoke the &lt;code&gt;Func&lt;/code&gt; that is in the value by parsing the &lt;code&gt;name&lt;/code&gt;, the &lt;code&gt;message&lt;/code&gt;, the &lt;code&gt;stack&lt;/code&gt;, and the original &lt;code&gt;JSException&lt;/code&gt; to this. If we could not find a matching key then we simply return a &lt;code&gt;WebIDLException&lt;/code&gt;. This is an &lt;code&gt;Exception&lt;/code&gt; type that we have implemented to give all &lt;code&gt;Exceptions&lt;/code&gt; from this &lt;code&gt;ErrorHandlingJSRuntime&lt;/code&gt; a common type they can be derived from.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;/// &amp;lt;summary&amp;gt;
/// A common exception class for all exceptions from the &amp;lt;c&amp;gt;Blazor.WebIDL&amp;lt;/c&amp;gt; library.
/// &amp;lt;/summary&amp;gt;
public class WebIDLException : Exception
{
    private readonly string? jSStackTrace;

    /// &amp;lt;summary&amp;gt;
    /// Returns the stack trace as a string. The stack is prepended with the JS stack trace if there is any. If no stack trace is available, null is returned.
    /// &amp;lt;/summary&amp;gt;
    public override string? StackTrace =&amp;gt; jSStackTrace is not null ? jSStackTrace + '\n' + base.StackTrace : base.StackTrace;

    /// &amp;lt;summary&amp;gt;
    /// Constructs a wrapper Exception for the given error.
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name=&amp;quot;message&amp;quot;&amp;gt;User agent-defined value that provides human readable details of the error.&amp;lt;/param&amp;gt;
    /// &amp;lt;param name=&amp;quot;jSStackTrace&amp;quot;&amp;gt;The stack trace from JavaScript if there is any.&amp;lt;/param&amp;gt;
    /// &amp;lt;param name=&amp;quot;innerException&amp;quot;&amp;gt;Inner exception which is the cause of this exception.&amp;lt;/param&amp;gt;
    public WebIDLException(string message, string? jSStackTrace, Exception innerException) : base(message, innerException)
    {
        this.jSStackTrace = jSStackTrace;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;WebIDLException&lt;/code&gt; has a single constructor that parses the &lt;code&gt;message&lt;/code&gt; and &lt;code&gt;innerException&lt;/code&gt; down to the base &lt;code&gt;Exception&lt;/code&gt; and sets the field &lt;code&gt;jSStackTrace&lt;/code&gt; with the potential stack trace. If the &lt;code&gt;jSStackTrace&lt;/code&gt; is not null then it is prepended to the base &lt;code&gt;StackTrace&lt;/code&gt;. Our goal is to map each standard error name to some &lt;code&gt;Exception&lt;/code&gt; type that inherits from &lt;code&gt;WebIDLException&lt;/code&gt;. For this purpose, we define the dictionary &lt;code&gt;ErrorMapper&lt;/code&gt; as a public property on the &lt;code&gt;ErrorHandlingJSRuntime&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public Dictionary&amp;lt;string, Func&amp;lt;string, string, string?, Exception, WebIDLException&amp;gt;&amp;gt; ErrorMapper { get; set; } = ErrorMappers.Default;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We define a static readonly default dictionary that maps the ones defined in the specifications. This opens up for anyone to add to the error mapper if they need to support some custom JS error type. There are a lot of predefined errors in JS as we have seen previously so I will not show all the different &lt;code&gt;Exception&lt;/code&gt; types I have created. But I have grouped them so that all the &lt;em&gt;Native Error Types&lt;/em&gt; like &lt;code&gt;ReferenceErrorException&lt;/code&gt; and &lt;code&gt;TypeErrorException&lt;/code&gt; are derived from a common &lt;code&gt;NativeErrorException&lt;/code&gt; and all the different &lt;code&gt;DOMException&lt;/code&gt;s are derived from a common &lt;code&gt;DOMException&lt;/code&gt; class.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;/// &amp;lt;summary&amp;gt;
/// An exception that encapsulates a name for an exception.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;remarks&amp;gt;&amp;lt;see href=&amp;quot;https://webidl.spec.whatwg.org/#idl-DOMException&amp;quot;&amp;gt;See the WebIDL definition here&amp;lt;/see&amp;gt;&amp;lt;/remarks&amp;gt;
public class DOMException : WebIDLException
{
    public const string NotFoundError = &amp;quot;NotFoundError&amp;quot;;
    public const string SyntaxError = &amp;quot;DOMExceptionSyntaxError&amp;quot;;
    public const string AbortError = &amp;quot;AbortError&amp;quot;;
    // Constants for all the other predefined `DOMException` names are also included here but have been omitted for brevity.

    /// &amp;lt;summary&amp;gt;
    /// Error name which should be one of the ones listed in this &amp;lt;see href=&amp;quot;https://webidl.spec.whatwg.org/#dfn-error-names-table&amp;quot;&amp;gt;error names table&amp;lt;/see&amp;gt;.
    /// &amp;lt;/summary&amp;gt;
    public string Name { get; set; }

    /// &amp;lt;summary&amp;gt;
    /// Constructs a wrapper Exception for the given error.
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name=&amp;quot;message&amp;quot;&amp;gt;User agent-defined value that provides human readable details of the error.&amp;lt;/param&amp;gt;
    /// &amp;lt;param name=&amp;quot;name&amp;quot;&amp;gt;Error name which should be one of the ones listed in this &amp;lt;see href=&amp;quot;https://webidl.spec.whatwg.org/#dfn-error-names-table&amp;quot;&amp;gt;error names table&amp;lt;/see&amp;gt;.&amp;lt;/param&amp;gt;
    /// &amp;lt;param name=&amp;quot;jSStackTrace&amp;quot;&amp;gt;The stack trace from JavaScript if there is any.&amp;lt;/param&amp;gt;
    /// &amp;lt;param name=&amp;quot;innerException&amp;quot;&amp;gt;Inner exception which is the cause of this exception.&amp;lt;/param&amp;gt;
    protected DOMException(string message, string name, string? jSStackTrace, Exception innerException) : base(message, jSStackTrace, innerException)
    {
        Name = name;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;DOMException&lt;/code&gt; defines a lot of constants for the different names of errors, but also adds a property &lt;code&gt;Name&lt;/code&gt; that defines the error name. The constructor is protected since one should always define a derived &lt;code&gt;Exception&lt;/code&gt; type for each additional custom &lt;code&gt;DOMException&lt;/code&gt;. A concrete example of a &lt;code&gt;DOMException&lt;/code&gt; could be the &lt;code&gt;NotFoundErrorException&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;/// &amp;lt;summary&amp;gt;
/// The object can not be found here.
/// &amp;lt;/summary&amp;gt;
/// &amp;lt;remarks&amp;gt;&amp;lt;see href=&amp;quot;https://webidl.spec.whatwg.org/#notfounderror&amp;quot;&amp;gt;See the WebIDL definition here&amp;lt;/see&amp;gt;&amp;lt;/remarks&amp;gt;
public class NotFoundErrorException : DOMException
{
    /// &amp;lt;summary&amp;gt;
    /// Constructs a wrapper Exception for the given error.
    /// &amp;lt;/summary&amp;gt;
    /// &amp;lt;param name=&amp;quot;message&amp;quot;&amp;gt;User agent-defined value that provides human readable details of the error.&amp;lt;/param&amp;gt;
    /// &amp;lt;param name=&amp;quot;jSStackTrace&amp;quot;&amp;gt;The stack trace from JavaScript if there is any.&amp;lt;/param&amp;gt;
    /// &amp;lt;param name=&amp;quot;innerException&amp;quot;&amp;gt;Inner exception which is the cause of this exception.&amp;lt;/param&amp;gt;
    public NotFoundErrorException(string message, string? jSStackTrace, Exception innerException) : base(message, NotFoundError, jSStackTrace, innerException) { }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now we are ready to define our &lt;code&gt;Default&lt;/code&gt; &lt;code&gt;ErrorMapper&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static class ErrorMappers
{
    public static Dictionary&amp;lt;string, Func&amp;lt;string, string, string?, Exception, WebIDLException&amp;gt;&amp;gt; Default { get; } = new()
    {
        { DOMException.NotFoundError, (name, message, jSStackTrace, innerException) =&amp;gt; new NotFoundErrorException(message, jSStackTrace, innerException) },
        { DOMException.SyntaxError, (name, message, jSStackTrace, innerException) =&amp;gt; new SyntaxErrorDOMException(message, jSStackTrace, innerException) },
        { DOMException.AbortError, (name, message, jSStackTrace, innerException) =&amp;gt; new AbortErrorException(message, jSStackTrace, innerException) },
        // Also includes all other DOMException types, but they have been omitted from the article for brevity again.
        { &amp;quot;EvalError&amp;quot;, (name, message, jSStackTrace, innerException) =&amp;gt; new EvalErrorException(message, jSStackTrace, innerException) },
        { &amp;quot;RangeError&amp;quot;, (name, message, jSStackTrace, innerException) =&amp;gt; new RangeErrorException(message, jSStackTrace, innerException) },
        { &amp;quot;ReferenceError&amp;quot;, (name, message, jSStackTrace, innerException) =&amp;gt; new ReferenceErrorException(message, jSStackTrace, innerException) },
        { &amp;quot;SyntaxError&amp;quot;, (name, message, jSStackTrace, innerException) =&amp;gt; new TypeErrorException(message, jSStackTrace, innerException) },
        { &amp;quot;TypeError&amp;quot;, (name, message, jSStackTrace, innerException) =&amp;gt; new TypeErrorException(message, jSStackTrace, innerException) },
        { &amp;quot;URIError&amp;quot;, (name, message, jSStackTrace, innerException) =&amp;gt; new URIErrorException(message, jSStackTrace, innerException) },
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;Default&lt;/code&gt; mapper is pretty straight forward as we would expect so now we are done with implementing the &lt;code&gt;ErrorHandlingJSRuntime&lt;/code&gt;. Let's make a small sample of using this to call a function that will fail and then catch the exception.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@inject IJSRuntime JSRuntime

&amp;lt;span style=&amp;quot;color: red;&amp;quot;&amp;gt;@exceptionMessage&amp;lt;/span&amp;gt;

@code {
    string? exceptionMessage;

    protected override async Task OnInitializedAsync()
    {
        try
        {
            var errorHandlingJSRuntime = new ErrorHandlingJSRuntime(JSRuntime);
            var url = await errorHandlingJSRuntime.InvokeAsync&amp;lt;string&amp;gt;(&amp;quot;URL.createObjectURL&amp;quot;, &amp;quot;not a blob&amp;quot;);
        }
        catch (TypeErrorException ex)
        {
            exceptionMessage = $&amp;quot;Wrong type for argument. {ex.Message}&amp;quot;;
        }
        catch (WebIDLException ex)
        {
            exceptionMessage = $&amp;quot;An {ex.GetType().Name} happened: \&amp;quot;{ex.Message}\&amp;quot;&amp;quot;;
        }
        catch (Exception)
        {
            exceptionMessage = &amp;quot;An unexpected error happened.&amp;quot;;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above example, we try to call the &lt;code&gt;createObjectURL&lt;/code&gt; method from the browser File API. This method takes a &lt;code&gt;Blob&lt;/code&gt;, a &lt;code&gt;File&lt;/code&gt;, or a &lt;code&gt;MediaSource&lt;/code&gt; as its argument, but we passed a string to it. So it throws a &lt;code&gt;TypeErrorException&lt;/code&gt; since there was no overload for the method taking a string. This results in the following message in our app:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-console"&gt;Wrong type for argument. Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.
&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;Blazor.WebIDL&lt;/h3&gt;
&lt;p&gt;As the JS errors are declared in the WebIDL standard specifications I have included the work that we have done above in my &lt;a href="https://github.com/KristofferStrube/Blazor.WebIDL"&gt;Blazor.WebIDL&lt;/a&gt; package. You can add it to your project by running the following in the terminal while located in the folder of your Blazor project.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet add package KristofferStrube.Blazor.WebIDL
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And instead of having to construct the &lt;code&gt;ErrorHandlingJSRuntime&lt;/code&gt; yourself, I've added an extension method that adds the needed services to the service collection so that you can just inject the &lt;code&gt;ErrorHandlingJSRuntime&lt;/code&gt; as an &lt;code&gt;IErrorHandlingJSRuntime&lt;/code&gt; in the pages that need it.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;builder.Services.AddErrorHandlingJSRuntime();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I've made some more simplifications that make it easier to use. But for this to work we also need to call and await the extension method &lt;code&gt;SetupErrorHandlingJSInterop&lt;/code&gt; on our &lt;code&gt;ServiceProvider&lt;/code&gt; after we have &lt;code&gt;Build&lt;/code&gt; the application, but before we run it.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var app = builder.Build();

await app.Services.SetupErrorHandlingJSInterop();

await app.RunAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can inject it into some Blazor page and use it directly to make invocations.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@inject IErrorHandlingJSRuntime ErrorHandlingJSRuntime

&amp;lt;button @onclick=&amp;quot;ReadClipboard&amp;quot;&amp;gt;Copy from clipboard&amp;lt;/button&amp;gt;
&amp;lt;br /&amp;gt;
@if (result is not null)
{
    &amp;lt;p&amp;gt;You have the text: '@result' in your clipboard&amp;lt;/p&amp;gt;
}
else
{
    &amp;lt;code&amp;gt;@copyError&amp;lt;/code&amp;gt;
}

@code {
    private string? result;
    private string copyError = string.Empty;

    private async Task Copy()
    {
        try
        {
            result = await ErrorHandlingJSRuntime.InvokeAsync&amp;lt;string&amp;gt;(&amp;quot;navigator.clipboard.readText&amp;quot;);
        }
        catch (NotAllowedErrorException)
        {
            copyError = &amp;quot;The user has not given permission to read the clipboard.&amp;quot;;
        }
        catch (DOMException exception)
        {
            copyError = $&amp;quot;{exception.Name} (which is a DOMException): \&amp;quot;{exception.Message}\&amp;quot;&amp;quot;;
        }
        catch (Exception)
        {
            copyError = &amp;quot;An unexpected error happened.&amp;quot;;
        }
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the above sample, we try to read the content of the clipboard. You might not have given the site permission to do this and in this case, a &lt;code&gt;NotAllowedErrorException&lt;/code&gt; will be thrown. If it wasn't this specific kind of &lt;code&gt;DOMException&lt;/code&gt; that happened then it might have been some other &lt;code&gt;DOMException&lt;/code&gt; type which is the next kind we try to catch and in the end, if it wasn't any of these we give back a general error message.&lt;/p&gt;
&lt;p&gt;I have also added error handling wrappers for the &lt;code&gt;IJSInProcessRuntime&lt;/code&gt;, &lt;code&gt;IJSObjectReference&lt;/code&gt;, and &lt;code&gt;IJSInProcessObjectReference&lt;/code&gt; types and made sure that you get back an appropriate error handling version of an &lt;code&gt;IJSObjectReference&lt;/code&gt; if this is the &lt;code&gt;TValue&lt;/code&gt; type that you return from an invocation. So in total, we now have the following error-handling JSInterop interfaces that we can inject.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;IErrorHandlingJSRuntime&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IErrorHandlingJSInProcessRuntime&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IErrorHandlingJSObjectReference&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;&lt;code&gt;IErrorHandlingJSInProcessObjectReference&lt;/code&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The reason why we need to call the &lt;code&gt;SetupErrorHandlingJSInterop&lt;/code&gt; was so that we could return &lt;code&gt;IErrorHandlingJSObjectReference&lt;/code&gt;s without having to resolve the helper asynchronously when creating it but also so that we can use the &lt;code&gt;IErrorHandlingJSInProcessRuntime&lt;/code&gt; when using Blazor WASM to make synchronous JS invocations without using some asynchronous factory to create the runtime each time it is needed.&lt;/p&gt;
&lt;h3&gt;Future plans&lt;/h3&gt;
&lt;p&gt;The plan is to also make an error-handling version of the &lt;code&gt;IJSUnmarshalledRuntime&lt;/code&gt; in the future. And I intend to upgrade all my existing browser API packages &lt;a href="https://github.com/KristofferStrube/Blazor.FileSystemAccess"&gt;Blazor.FileSystemAccess&lt;/a&gt;, &lt;a href="https://github.com/KristofferStrube/Blazor.FileSystem"&gt;Blazor.FileSystem&lt;/a&gt;, &lt;a href="https://github.com/KristofferStrube/Blazor.FileAPI"&gt;Blazor.FileAPI&lt;/a&gt;, &lt;a href="https://github.com/KristofferStrube/Blazor.Streams"&gt;Blazor.Streams&lt;/a&gt;, &lt;a href="https://github.com/KristofferStrube/Blazor.CompressionStreams"&gt;Blazor.CompressionStreams&lt;/a&gt;, &lt;a href="https://github.com/KristofferStrube/Blazor.WebAudio"&gt;Blazor.WebAudio&lt;/a&gt;, &lt;a href="https://github.com/KristofferStrube/Blazor.MediaCaptureStreams"&gt;Blazor.MediaCaptureStreams&lt;/a&gt;, &lt;a href="https://github.com/KristofferStrube/Blazor.DOM"&gt;Blazor.DOM&lt;/a&gt; and all my future ones to use the error-handling versions when they make JSInterop calls.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;At the start of this post, we saw some of the existing approaches for handling different types of exceptions when making JSInterop calls in Blazor. And we now know that this is because we don't get the name of the exception extracted when the error is returned to C#. We then found a way to catch more details of the error on the JS side of the invocation and re-throw these details back to Blazor where we could then unpack the name, message, and stack trace separately and map them to custom C# exception types. In the end, I presented the &lt;a href="https://github.com/KristofferStrube/Blazor.WebIDL"&gt;Blazor.WebIDL&lt;/a&gt; package that has made this &lt;code&gt;ErrorHandlingJSRuntime&lt;/code&gt; easy to add to any project where you want to have typed exceptions when using JSInterop. We also went through a small example of using the package to make handle different error scenarios. I would be very happy if people want to test the package and give feedback on the GitHub repository so that I can make sure that it is both easy and safe to use. The library is still in alpha while I test it in some of my libraries, but there won't go long before I make a first full release. And as always, if you have any feedback or questions for this post then feel free to reach out to me on Mastodon or Twitter.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">typed-signalr-clients-making-type-safe-real-time-communication-in-dotnet</guid><link>https://kristoffer-strube.dk/post/typed-signalr-clients-making-type-safe-real-time-communication-in-dotnet/</link><title>Typed SignalR Clients - Making type-safe real-time communication in .NET</title><description>A small feature that was introduced during one of the previews of .NET 7 was typed SignalR clients enabling us to develop real-time tools with type safety. This means that we can make a shared contract that defines which methods can be invoked on the SignalR hub and which can be invoked on the SignalR Client. This is possible by using source generators which have been a huge focus in .NET 7 in general. In this post, we will make a sticky note bulletin board in Blazor WASM and add real-time collaboration to it using a typed SignalR client to showcase how to use typed SignalR clients.</description><pubDate>Thu, 06 Apr 2023 00:00:00 +0200</pubDate><a10:updated>2023-04-06T00:00:00+02:00</a10:updated><a10:content type="text">&lt;h3&gt;Project features&lt;/h3&gt;
&lt;p&gt;Before getting started with the actual project I want to outline the features that I would like for our real-time sticky note bulletin board.&lt;/p&gt;
&lt;h4&gt;Creating sticky notes&lt;/h4&gt;
&lt;p&gt;First of all, I would like to be able to create sticky notes and present them on a screen as yellow squares with text on them.&lt;/p&gt;
&lt;h4&gt;Writing text&lt;/h4&gt;
&lt;p&gt;I would like to be able to edit the text on the notes and for these changes to propagate to other users in real-time.&lt;/p&gt;
&lt;h4&gt;Moving notes&lt;/h4&gt;
&lt;p&gt;I would also like to be able to move each sticky note to be able to communicate association by proximity or order.&lt;/p&gt;
&lt;h4&gt;Deletion&lt;/h4&gt;
&lt;p&gt;After having written on the sticky notes I would like to be able to delete them again. I also want an option for deleting all sticky notes to clear the board.&lt;/p&gt;
&lt;h4&gt;Locking&lt;/h4&gt;
&lt;p&gt;When multiple users can move, write, and delete the same sticky notes then I would also like users to get a lock on a sticky note while they edit it i.e. so that others can intervene or move the note away while the user is writing on it. &lt;em&gt;A disclaimer is that the only system concurrency-related education I have is on peer-2-peer systems and blockchains so I wouldn't use the same approaches as I have employed on a large scale system as they simply are my loose ideas for a concurrency heuristic.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The following image is a rough mockup of what I would like it to look like. But we might make changes as we go.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/mock.png" alt="Drawn mockup of what we would like the bulletinboard to look like." /&gt;&lt;/p&gt;
&lt;h3&gt;Solution structure&lt;/h3&gt;
&lt;p&gt;The solution will consist of three different projects that we need to make our bulletin board functional. We start by making a new folder and then a new solution file.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;mkdir DistributedStickyNotes
cd DistributedStickyNotes
dotnet new sln
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Shared&lt;/h4&gt;
&lt;p&gt;We need a small shared project in the form of a Class Library. This will hold the interfaces which define which methods the clients and the hub has and it will define our &lt;code&gt;Note&lt;/code&gt; class which is what we will use to represent each sticky note. We create this project in a new sub-folder called &lt;code&gt;Shared/&lt;/code&gt; and then we will get back to filling out the actual content for it later.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;mkdir Shared
cd Shared
dotnet new classlib
cd ..
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Client&lt;/h4&gt;
&lt;p&gt;Next, we need the actual Blazor WASM client. This will present the sticky notes to each user and use the generated typed SignalR client. This is a really simple project with a single purpose so for this, we use the empty variant of the Blazor WASM templates.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;mkdir Client
cd Client
dotnet new blazorwasm-empty
cd ..
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Server&lt;/h4&gt;
&lt;p&gt;The last part is the server that will host our SignalR hub enabling each client to communicate indirectly. It will also maintain the state of the bulletin board being our single source of truth both with regards to the information about each note and its locks. For this, we create an ASP.NET API using the Minimal API template.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;mkdir Server
cd Server
dotnet new webapi -minimal true
cd ..
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then in the end we add references to all the projects from the solution and add references to the Shared project from both the Client and Server projects.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet sln add Shared
dotnet sln add Client
dotnet sln add Server
cd Client
dotnet add reference ../Shared
cd ../Server
dotnet add reference ../Shared
cd ..
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we can start coding in whatever IDE is your favourite. Be that Rider, Visual Studio, Emacs, or something else.&lt;/p&gt;
&lt;h3&gt;Communication interface&lt;/h3&gt;
&lt;p&gt;The first part we create is the interfaces that define the contract that the clients and the server will follow to communicate with each other. Before we make the interfaces we create the model that will represent a sticky note as we will use this to parse information between the client and the server. We create this in the Shared project.&lt;/p&gt;
&lt;h5&gt;Note.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class Note
{
    public Note() { }

    public Note(double x, double y)
    {
        Id = Guid.NewGuid();
        LastEdited = DateTimeOffset.UtcNow;
        X = x;
        Y = y;
    }

    public Guid Id { get; set; }
    public string Text { get; set; } = string.Empty;
    public double X { get; set; }
    public double Y { get; set; }
    public string? LastLockingUser { get; set; }
    public DateTimeOffset LastEdited { get; set; }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We define two constructors. One that is empty will be used for deserializing the model when receiving it. The second constructor takes a position as we always want to know where to place a new Note. This also sets the &lt;code&gt;Id&lt;/code&gt; to a new random &lt;code&gt;Guid&lt;/code&gt; which we will use to identify the &lt;code&gt;Note&lt;/code&gt;. It also defines that the &lt;code&gt;Note&lt;/code&gt; has been edited right now as it was constructed. We have some other properties that we don't set in the constructor. We have the &lt;code&gt;Text&lt;/code&gt; which is what will be displayed on the &lt;code&gt;Note&lt;/code&gt; and we have &lt;code&gt;LastLockingUser&lt;/code&gt; which we will use to identify which user currently has the lock for editing this.&lt;/p&gt;
&lt;p&gt;Next, we create the interface for which methods we want available to be able to call on the hub from the client.&lt;/p&gt;
&lt;h5&gt;IStickyNoteHub.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public interface IStickyNoteHub
{
    Task&amp;lt;List&amp;lt;Note&amp;gt;&amp;gt; LoadNotes();
    Task CreateNote(double x, double y);
    Task UpdateNoteText(Guid id, string text);
    Task&amp;lt;bool&amp;gt; LockNote(Guid id);
    Task MoveNote(Guid id, double x, double y);
    Task ClearNotes();
    Task DeleteNote(Guid id);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The methods all return &lt;code&gt;Task&lt;/code&gt; as this communication is inherently asynchronous. Each of these is represented by one of the features that we wanted for our bulletin board.&lt;/p&gt;
&lt;p&gt;We also have communication the other way i.e. server-to-client. For this, we create another interface representing the client and the things it will listen for.&lt;/p&gt;
&lt;h5&gt;IStickyNoteClient.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public interface IStickyNoteClient
{
    Task NoteCreated(Note note);
    Task NoteUpdated(Note note);
    Task NoteDeleted(Guid id);
} 
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;These likewise return &lt;code&gt;Task&lt;/code&gt; and enable the client to react to new notes being created, any changes being broadcasted, and when notes are being deleted.&lt;/p&gt;
&lt;h3&gt;Typed SignalR Hub&lt;/h3&gt;
&lt;p&gt;The next part is to implement the SignalR hub in the Server project. For this, we use the existing strongly typed Hub class and implement the &lt;code&gt;IStickyNoteHub&lt;/code&gt; interface.&lt;/p&gt;
&lt;h5&gt;StickyNoteHub.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class StickyNoteHub : Hub&amp;lt;IStickyNoteClient&amp;gt;, IStickyNoteHub
{
    // We will add the required methods here.
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Before we actually implement the methods of the &lt;code&gt;IStickyNoteHub&lt;/code&gt; interface we will need somewhere to store our sticky notes. We will make a very simple static class with a static collection in it. We could have used some service for managing this, but for the purpose of this demo, we will be okay with this.&lt;/p&gt;
&lt;h5&gt;StaticStorage.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static class StaticStorage
{
    public static List&amp;lt;Note&amp;gt; Notes { get; set; } = new();
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we are ready to implement each of the methods from our hub interface. First, we implement &lt;code&gt;LoadNotes&lt;/code&gt; which is really simple to implement given our &lt;code&gt;StaticStorage&lt;/code&gt; collection.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public Task&amp;lt;List&amp;lt;Note&amp;gt;&amp;gt; LoadNotes()
{
    return Task.FromResult(StaticStorage.Notes);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we add the method for adding a new Note &lt;code&gt;CreateNote&lt;/code&gt;. This will create a new &lt;code&gt;Note&lt;/code&gt; and notify all connected users that it was added by invoking the &lt;code&gt;IStickyNoteClient&lt;/code&gt; method &lt;code&gt;NoteCreated&lt;/code&gt;. When notifying that it was created it also sends the &lt;code&gt;Note&lt;/code&gt; object itself so that the clients have a local copy of it. Together with the &lt;code&gt;LoadNotes&lt;/code&gt; method, this ensures that each user has a local copy of all sticky notes that were created before the user joined and all notes that were created later.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task CreateNote(double x, double y)
{
    var newNote = new Note(x, y);
    StaticStorage.Notes.Add(newNote);
    await Clients.All.NoteCreated(newNote);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Next, we implement the method for updating the text of a sticky note.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task UpdateNoteText(Guid id, string text)
{
    if (StaticStorage.Notes.FirstOrDefault(note =&amp;gt; note.Id == id) is not { } serverNote) return;
    if (!serverNote.TryLock(Context.ConnectionId, Clients.Others))
    {
        await Clients.Caller.NoteUpdated(serverNote);
        return;
    }

    serverNote.Text = text;
    await Clients.Others.NoteUpdated(serverNote);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method takes the &lt;code&gt;id&lt;/code&gt; of an existing sticky note and the updated &lt;code&gt;text&lt;/code&gt;. We first go through all the notes and find the one that has a matching &lt;code&gt;Id&lt;/code&gt;. If there was none then we simply return as the client must then have been in some bad state which we should ignore. Then we call the method &lt;code&gt;TryLock&lt;/code&gt; on the &lt;code&gt;Note&lt;/code&gt; which tries to lock it so that only this user can change it for a short time. If it was not successful in locking the sticky note then we update the original calling client with the current state of the note so that it knows that this is locked by someone else. If it was successful then we update the text and notify all other clients of this new state. We don't need to notify the original client in this case as it will optimistically assume that it could get the lock. Let's implement the &lt;code&gt;TryLock&lt;/code&gt; method on the &lt;code&gt;Note&lt;/code&gt; class now while we are here. To implement this we first need two secondary methods which will become useful in other scenarios: A method for checking if a user &lt;em&gt;can&lt;/em&gt; lock and a method that sets the lock. We add the following method called &lt;code&gt;CanLock&lt;/code&gt; to our &lt;code&gt;Note&lt;/code&gt; model.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public bool CanLock(string? connectionId) =&amp;gt;
    DateTimeOffset.UtcNow.Subtract(LastEdited).TotalSeconds &amp;gt; 1
    || LastLockingUser is null
    || LastLockingUser == connectionId;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method takes a &lt;code&gt;connectionId&lt;/code&gt; which is the id of the user that wants to lock. We state that the &lt;code&gt;Note&lt;/code&gt; can be locked if it hasn't been edited within the last second, if no user has locked it before, or if the last user that locked it was the one with &lt;code&gt;connectionId&lt;/code&gt;. And then we create the method that actually locks. It does so by updating the time of the last edit to the current time and by setting the locking user to the given one.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public void Lock(string? connectionId)
{
    LastLockingUser = connectionId;
    LastEdited = DateTimeOffset.UtcNow;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Now, we can implement the &lt;code&gt;TryLock&lt;/code&gt; method.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private CancellationTokenSource? cts;

public bool TryLock(string? connectionId, IStickyNoteClient others)
{
    lock (this)
    {
        if (!CanLock(connectionId)) return false;
        Lock(connectionId);

        cts?.Cancel();
        if (others is null) return true;

        cts = new CancellationTokenSource();
        ThreadPool.QueueUserWorkItem(new WaitCallback(async parameter =&amp;gt;
        {
            CancellationToken token = (CancellationToken)parameter!;
            await Task.Delay(1000);
            if (token.IsCancellationRequested) return;
            await others.NoteUpdated(this);
        }), cts.Token);
        return true;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It first locks the &lt;code&gt;Note&lt;/code&gt; which is simply to enforce that only one user can try to lock a &lt;code&gt;Note&lt;/code&gt; at any one point. Then we check if the user can lock the &lt;code&gt;Note&lt;/code&gt; and if it can't then we return false. Else we lock it. The user will have a valid lock for &lt;code&gt;1&lt;/code&gt; second and after this everyone should know that the sticky note is no longer locked. We do this by waiting for &lt;code&gt;1 second&lt;/code&gt; on a new thread and then updating all other users with the new state. We only start this thread if the others parameter is not &lt;code&gt;null&lt;/code&gt; indicating that we want to be informed once the note is free again. We parse a &lt;code&gt;CancellationToken&lt;/code&gt; to the new thread which we will use to cancel notifying all other users if the lock has been reacquired within the &lt;code&gt;1 second&lt;/code&gt; which would extend its lock time so that we don't spam all other users with updates of the lock state while the user is continuously editing.&lt;/p&gt;
&lt;p&gt;Next, we need to handle the moving of a sticky note. This will often be a long continuous set of updated coordinates over a period of time. For this reason, we want to ensure that the client has the lock for a note before they are allowed to start moving it. So now, we implement the hub method &lt;code&gt;LockNote&lt;/code&gt; which returns a boolean indicating whether the &lt;code&gt;Note&lt;/code&gt; was locked.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task&amp;lt;bool&amp;gt; LockNote(Guid id)
{
    if (StaticStorage.Notes.FirstOrDefault(note =&amp;gt; note.Id == id) is not { } serverNote)
        return false;

    if (!serverNote.TryLock(Context.ConnectionId, Clients.Others))
        return false;

    await Clients.Others.NoteUpdated(serverNote);
    return true;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Alike &lt;code&gt;UpdateNoteText&lt;/code&gt; it also has the &lt;code&gt;id&lt;/code&gt; of a sticky note as its parameter and tries to find that specific &lt;code&gt;Note&lt;/code&gt; from our static collection. Next, if it did find it, it also tries to lock it and notify all other clients it was successful as that would change the state of the note. The client will wait for it to return before it continues and if it returns true then it knows that it can move the sticky note. The clients then update the position of the note by calling the &lt;code&gt;MoveNote&lt;/code&gt; hub method.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task MoveNote(Guid id, double x, double y)
{
    if (StaticStorage.Notes.FirstOrDefault(note =&amp;gt; note.Id == id) is not { } serverNote) return;
    if (!serverNote.TryLock(Context.ConnectionId, Clients.Others))
    {
        await Clients.Caller.NoteUpdated(serverNote);
        return;
    }

    serverNote.X = x;
    serverNote.Y = y;
    await Clients.Others.NoteUpdated(serverNote);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It likewise finds the relevant &lt;code&gt;Note&lt;/code&gt; and tries to lock it. In this case, it will likely extend its lock by calling &lt;code&gt;TryLock&lt;/code&gt; unless it eventually hasn't moved for a full second which will make it possible for others to get the lock instead. If it is successful in getting or extending its lock then it updates the position of the note as expected and updates all other users of this update else it notifies the original caller of the updated state.&lt;/p&gt;
&lt;p&gt;Finally, we just need to implement the methods for deleting a single note and clearing all notes.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task DeleteNote(Guid id)
{
    if (StaticStorage.Notes.FirstOrDefault(note =&amp;gt; note.Id == id) is not { } serverNote)
        return;

    if (!serverNote.TryLock(Context.ConnectionId))
        return;

    StaticStorage.Notes.Remove(serverNote);
    await Clients.All.NoteDeleted(id);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;What we do in this method is very similar to what we do in the other method except for the fact that we do not want to update all other users once the lock is no longer active as the &lt;code&gt;Note&lt;/code&gt; will have been removed at that point.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;ClearNotes&lt;/code&gt; hub method is the last one we need before having implemented &lt;code&gt;IStickyNoteHub&lt;/code&gt; fully. It simply goes through the &lt;code&gt;Id&lt;/code&gt;s of all sticky notes and tries to delete them one by one. It can likewise only delete each individual sticky note if no other user has a lock on it.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task ClearNotes()
{
    var noteIds= StaticStorage.Notes.Select(note =&amp;gt; note.Id).ToList();
    foreach (var id in noteIds)
    {
        await DeleteNote(id);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;While we are in the Server project let's configure the hub so that it is ready to be connected. We do this in &lt;code&gt;Program.cs&lt;/code&gt; by calling &lt;code&gt;AddSignalR&lt;/code&gt; on our service collection and by mapping our typed SignalR hub to an endpoint. I've made a very minimal setup like this:&lt;/p&gt;
&lt;h5&gt;Programs.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var builder = WebApplication.CreateBuilder(args);

builder.Services.AddSignalR();
builder.Services.AddCors();

var app = builder.Build();

app.UseHttpsRedirection();
app.UseCors(builder =&amp;gt; builder.WithOrigins(&amp;quot;https://localhost:7171&amp;quot;)
    .AllowAnyMethod()
    .AllowAnyHeader());
app.MapHub&amp;lt;StickyNoteHub&amp;gt;(&amp;quot;/stickynotehub&amp;quot;);

app.Run();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;I added the CORS settings as well since we are going to connect to the hub from our browser which expects CORS headers.&lt;/p&gt;
&lt;h3&gt;Typed SignalR Client&lt;/h3&gt;
&lt;p&gt;Now we are ready to implement our &lt;code&gt;IStickyNoteClient&lt;/code&gt; client interface. This is where all the new stuff happens which enables us to make typed calls to the hub to listen to events from the hub with relative ease. We first need to add two packages to our Blazor project.&lt;/p&gt;
&lt;p&gt;First, the client library for SignalR&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet add package Microsoft.AspNetCore.SignalR.Client
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then the source generator package that will make our life a piece of cake. This package is only available as a preview package as it was created during the preview development of .NET 7.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet add package Microsoft.AspNetCore.SignalR.Client.SourceGenerator
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Source generation setup&lt;/h4&gt;
&lt;p&gt;To bootstrap the source generation for our typed client and hub proxy we need to define two attributes that the source generator package will use to target certain partial methods.&lt;/p&gt;
&lt;h5&gt;HubServerProxyAttribute.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[AttributeUsage(AttributeTargets.Method)]
internal class HubServerProxyAttribute : Attribute { }
&lt;/code&gt;&lt;/pre&gt;
&lt;h5&gt;HubClientProxyAttribute.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[AttributeUsage(AttributeTargets.Method)]
internal class HubClientProxyAttribute : Attribute { }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The attributes are then used on these partial extension methods which make the source generator generate separate partial methods.&lt;/p&gt;
&lt;h5&gt;HubConnectionExtensions.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public static partial class HubConnectionExtensions
{
    [HubClientProxy]
    public static partial IDisposable ClientRegistration&amp;lt;T&amp;gt;(this HubConnection connection, T provider);

    [HubServerProxy]
    public static partial T ServerProxy&amp;lt;T&amp;gt;(this HubConnection connection);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Implementing IStickyNoteClient&lt;/h4&gt;
&lt;p&gt;Then we are ready to make the code-behind for our &lt;code&gt;Index.razor&lt;/code&gt; page. We start off by defining some fields and overriding the &lt;code&gt;OnInitializedAsync&lt;/code&gt; method which will be called once the page is created.&lt;/p&gt;
&lt;h5&gt;Index.razor.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public partial class Index : IStickyNoteClient
{
    private List&amp;lt;Note&amp;gt; notes = new();
    private IStickyNoteHub hubProxy = default!;
    private HubConnection connection = default!;

    protected override async Task OnInitializedAsync()
    {
        connection = new HubConnectionBuilder()
            .WithUrl(&amp;quot;https://localhost:7210/stickynotehub&amp;quot;)
            .Build();
        hubProxy = connection.ServerProxy&amp;lt;IStickyNoteHub&amp;gt;();
        _ = connection.ClientRegistration&amp;lt;IStickyNoteClient&amp;gt;(this);
        await connection.StartAsync();

        notes = await hubProxy.LoadNotes();
    }

    // Methods that implement IStickyNoteClient will be inserted below here
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The first thing we did was to make the page implement &lt;code&gt;IStickyNoteClient&lt;/code&gt;. We do this as we want the page itself to be what handles all notifications from the hub. Then we add a couple of fields. First a list of notes which will be our local copy of all notes and after this two fields that represent the abstraction of our hub which we can use to call the methods of the hub and the connection itself. In &lt;code&gt;OnInitializedAsync&lt;/code&gt; we create the connection and make a proxy for our hub using the extension method we defined earlier. Then we register our page as the client for our connection, start the connection, and load all the initial notes if there are any.&lt;/p&gt;
&lt;p&gt;Then we just need to implement the methods which &lt;code&gt;IStickyNoteClient&lt;/code&gt; defines to handle the events coming from the hub. We start off with &lt;code&gt;NoteCreated&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public Task NoteCreated(Note note)
{
    notes.Add(note);
    StateHasChanged();
    return Task.CompletedTask;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We simply add the new note to our local collection and update and force the UI to update by invoking &lt;code&gt;StateHasChanged&lt;/code&gt;. Then we implement the method for handling a sticky note that has been updated.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public Task NoteUpdated(Note note)
{
    if (notes.FirstOrDefault(n =&amp;gt; n.Id == note.Id) is not { } localNote)
        return Task.CompletedTask;

    localNote.Text = note.Text;
    localNote.X = note.X;
    localNote.Y = note.Y;
    localNote.LastLockingUser = note.LastLockingUser;
    localNote.LastEdited = note.LastEdited;

    StateHasChanged();
    return Task.CompletedTask;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This should look very familiar by now. We find the relevant &lt;code&gt;Note&lt;/code&gt;, update its properties, and update the UI.
The last method which handles when a note is deleted is likewise pretty simple:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public Task NoteDeleted(Guid id)
{
    if (notes.FirstOrDefault(n =&amp;gt; n.Id == id) is not { } localNote)
        return Task.CompletedTask;

    notes.Remove(localNote);
    StateHasChanged();
    return Task.CompletedTask;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Handling pointer events&lt;/h4&gt;
&lt;p&gt;Next we need to add a couple of methods to the code-behind that will handle different kinds of user input when the user moves the sticky notes around. We first write the method that handles when a sticky note starts being moved.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;private (double x, double y)? anchor;
private Note? editNote;

public async Task Down(Note note, PointerEventArgs eventArgs)
{
    if (!await hubProxy.LockNote(note.Id)) return;

    note.Lock(connection.ConnectionId);
    anchor = (eventArgs.ClientX, eventArgs.ClientY);
    editNote = note;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It first tries to get a lock at the server through the hub proxy. If it got it then we lock our local copy and set two fields that we have added which will manage which note we are currently moving and what position the pointer was at the last time we saw it.&lt;/p&gt;
&lt;p&gt;Next, we need to define what happens when we move the pointer.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task Move(PointerEventArgs eventArgs)
{
    if (anchor is not (double x, double y) || editNote is null || !editNote.CanLock(connection.ConnectionId))
        return;

    editNote.X += eventArgs.ClientX - x;
    editNote.Y += eventArgs.ClientY - y;
    editNote.LastEdited = DateTimeOffset.UtcNow;
    anchor = (eventArgs.ClientX, eventArgs.ClientY);
    await hubProxy.MoveNote(editNote.Id, editNote.X, editNote.Y);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;If either the anchor is &lt;code&gt;null&lt;/code&gt;, there is no current edit &lt;code&gt;Note&lt;/code&gt;, or if we can't lock the current &lt;code&gt;Note&lt;/code&gt; then we do nothing. But if we were successful then we update the position of the current &lt;code&gt;Note&lt;/code&gt; and when it was last edited which extends our local lock. We also update the anchor with our last position and finally tell the hub that we have moved the sticky note.&lt;/p&gt;
&lt;p&gt;Then we just need to handle when the pointer is raised which means the user is done moving the sticky note.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public async Task Up(PointerEventArgs eventArgs)
{
    await Move(eventArgs);

    anchor = null;
    editNote = null;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first invoke the &lt;code&gt;Move&lt;/code&gt; method to update the position for the last time before setting both the edit &lt;code&gt;Note&lt;/code&gt; and the anchor to &lt;code&gt;null&lt;/code&gt;. Then we are ready to write the markup that will present the actual sticky notes.&lt;/p&gt;
&lt;h4&gt;Presenting the sticky notes&lt;/h4&gt;
&lt;p&gt;We first need a little CSS for our text and our markers (delete cross and pin). We added the following to the &lt;code&gt;app.css&lt;/code&gt; file of our project which was pre-generated with the template.&lt;/p&gt;
&lt;h5&gt;app.css&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-CSS"&gt;html, body, #app, main {
    margin: 0;
    width: 100%;
    height: 100%;
    overflow:hidden;
}

.note-textarea {
    min-width: 100%;
    min-height: 100%;
    max-width: 100%;
    max-height: 100%;
    background-color: transparent;
    border: 0;
    font-family: cursive;
    font-size: 20px;
}

    .note-textarea:focus {
        outline: none;
    }

.note-markers {
    font-size: 20px;
    user-select: none;
    touch-action: none;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This will remove all margins and make use of the full width of the page all the way down to our page and the note-related classes are simple styling for our text and markers.&lt;/p&gt;
&lt;p&gt;Then we can begin to fill out our page.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-html"&gt;@page &amp;quot;/&amp;quot;

&amp;lt;button @onclick=&amp;quot;() =&amp;gt; hubProxy.CreateNote(10, 10)&amp;quot;&amp;gt;Create Sticky Note ➕&amp;lt;/button&amp;gt;
&amp;lt;button @onclick=&amp;quot;hubProxy.ClearNotes&amp;quot;&amp;gt;Clear All Sticky Notes ❌&amp;lt;/button&amp;gt;

&amp;lt;svg width=&amp;quot;100%&amp;quot; height=&amp;quot;100%&amp;quot;
     @onpointerup=&amp;quot;Up&amp;quot;
     @onpointerleave=&amp;quot;Up&amp;quot;
     @onpointermove=&amp;quot;Move&amp;quot;&amp;gt;
    &amp;lt;defs&amp;gt;
        &amp;lt;filter id=&amp;quot;shadow&amp;quot; x=&amp;quot;0&amp;quot; y=&amp;quot;0&amp;quot; width=&amp;quot;200%&amp;quot; height=&amp;quot;200%&amp;quot;&amp;gt;
            &amp;lt;feOffset result=&amp;quot;offOut&amp;quot; in=&amp;quot;SourceAlpha&amp;quot; dx=&amp;quot;5&amp;quot; dy=&amp;quot;5&amp;quot; /&amp;gt;
            &amp;lt;feGaussianBlur result=&amp;quot;blurOut&amp;quot; in=&amp;quot;offOut&amp;quot; stdDeviation=&amp;quot;2.5&amp;quot; /&amp;gt;
            &amp;lt;feBlend in=&amp;quot;SourceGraphic&amp;quot; in2=&amp;quot;blurOut&amp;quot; mode=&amp;quot;normal&amp;quot; /&amp;gt;
        &amp;lt;/filter&amp;gt;
    &amp;lt;/defs&amp;gt;

    @foreach (var note in notes)
    {
        &amp;lt;!-- This is where each Note will be drawn --&amp;gt;
    }

&amp;lt;/svg&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We make two buttons in the top of the page for creating new sticky notes and clearing all notes. After this, we make an SVG tag that fills the rest of the page. The tag has three event handlers. Two for when the pointer is raised or the pointer leaves the SVG tag which will trigger the &lt;code&gt;Up&lt;/code&gt; method and end the current drag if there is any. The third event handler is for when we move the pointer which will move the &lt;code&gt;Note&lt;/code&gt; if there is an active drag. Inside the SVG tag, we define a filter that makes a blurred black-and-white shadow that we use on each &lt;code&gt;Note&lt;/code&gt; and then we loop over all the notes and present them each individually.&lt;/p&gt;
&lt;p&gt;For each &lt;code&gt;Note&lt;/code&gt; we first draw a rectangle that represents the piece of paper. There is nothing special about it except for the fact that it changes color if the user can't lock the card. We also apply the filter that we defined before.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;rect fill=&amp;quot;@(note.CanLock(connection.ConnectionId) ? &amp;quot;#FFFF8F&amp;quot; : &amp;quot;#FFDF8F&amp;quot;)&amp;quot;
        x=&amp;quot;@note.X.AsString()&amp;quot;
        y=&amp;quot;@note.Y.AsString()&amp;quot;
        stroke=&amp;quot;#DDDD80&amp;quot;
        stroke-width=&amp;quot;1&amp;quot;
        width=&amp;quot;200px&amp;quot;
        height=&amp;quot;200px&amp;quot;
        filter=&amp;quot;url(#shadow)&amp;quot;&amp;gt;
&amp;lt;/rect&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we need to draw the text that is on the &lt;code&gt;Note&lt;/code&gt;. For this, we use the &lt;code&gt;foreignObject&lt;/code&gt; SVG tag enables us to inline HTML in SVG which we use to inline a &lt;code&gt;textarea&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;foreignObject x=&amp;quot;@note.X&amp;quot; y=&amp;quot;@((note.Y+30).AsString())&amp;quot; width=&amp;quot;200px&amp;quot; height=&amp;quot;170px&amp;quot;&amp;gt;
    &amp;lt;textarea @bind=note.Text
                    @bind:event=&amp;quot;oninput&amp;quot;
                    @bind:after=&amp;quot;() =&amp;gt; hubProxy.UpdateNoteText(note.Id, note.Text)&amp;quot;
                    disabled=&amp;quot;@(!note.CanLock(connection.ConnectionId))&amp;quot;
                    class=&amp;quot;note-textarea&amp;quot;&amp;gt;
    &amp;lt;/textarea&amp;gt;
&amp;lt;/foreignObject&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We place the &lt;code&gt;foreignObject&lt;/code&gt; in the same place as the rectangle, but shift it down &lt;code&gt;30 pixels&lt;/code&gt; to make space for the markers at the top. We then &lt;code&gt;@bind&lt;/code&gt; the value of the &lt;code&gt;textarea&lt;/code&gt; to the text of &lt;code&gt;Note&lt;/code&gt; and change the binding event to be &lt;code&gt;oninput&lt;/code&gt; instead of the default &lt;code&gt;onchange&lt;/code&gt; to make updates trigger more often. Then we use the new &lt;code&gt;@bind:after&lt;/code&gt; event to call &lt;code&gt;UpdateNoteText&lt;/code&gt; on our hub which will trigger every time the text of the &lt;code&gt;Note&lt;/code&gt; has been updated locally. We also disable the &lt;code&gt;textarea&lt;/code&gt; itself if the &lt;code&gt;Note&lt;/code&gt; can't be locked by the user.&lt;/p&gt;
&lt;p&gt;The last part is just to make our two markers. For this, we use the SVG tag &lt;code&gt;text&lt;/code&gt;. This tag has been overloaded by &lt;code&gt;Blazor&lt;/code&gt; to explicitly start a markup section so we need to embed our two markers in an extra &lt;code&gt;text&lt;/code&gt; tag.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;text&amp;gt;
    &amp;lt;text @onclick=&amp;quot;() =&amp;gt; hubProxy.DeleteNote(note.Id)&amp;quot;
            x=&amp;quot;@note.X.AsString()&amp;quot;
            y=&amp;quot;@note.Y.AsString()&amp;quot;
            alignment-baseline=&amp;quot;before-edge&amp;quot;
            class=&amp;quot;note-markers&amp;quot;
            style=&amp;quot;pointer-events:@(note.CanLock(connection.ConnectionId) ? &amp;quot;inherit&amp;quot; : &amp;quot;none&amp;quot;)&amp;quot;&amp;gt;
        ❌
    &amp;lt;/text&amp;gt;
    &amp;lt;text @onpointerdown=&amp;quot;e =&amp;gt; Down(note, e)&amp;quot;
            x=&amp;quot;@((note.X+180).AsString())&amp;quot;
            y=&amp;quot;@note.Y.AsString()&amp;quot;
            alignment-baseline=&amp;quot;before-edge&amp;quot;
            class=&amp;quot;note-markers&amp;quot;
            style=&amp;quot;pointer-events:@(note.CanLock(connection.ConnectionId) ? &amp;quot;inherit&amp;quot; : &amp;quot;none&amp;quot;)&amp;quot;&amp;gt;
        @(note == editNote || !note.CanLock(connection.ConnectionId) ? &amp;quot;📌&amp;quot; : &amp;quot;📍&amp;quot;)
    &amp;lt;/text&amp;gt;
&amp;lt;/text&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We place the delete cross on the left side of the &lt;code&gt;Note&lt;/code&gt; and the pin on the right side. Both use &lt;code&gt;alignment-baseline=&amp;quot;before-edge&amp;quot;&lt;/code&gt; which aligns them towards the top of the &lt;code&gt;Note&lt;/code&gt; vertically. For both markers, we disable pointer events if the &lt;code&gt;Note&lt;/code&gt; can't be locked by the user. We also add that the pin switches icon if we ourselves are currently moving it or if someone else has the lock on it.&lt;/p&gt;
&lt;p&gt;Then we are done and we can enjoy the result of our work in this short video.&lt;/p&gt;
&lt;video width="700" autoplay muted controls loop&gt;
&lt;source src="https://kristoffer-strube.dk/videos/sticky-notes-demo.mp4" type="video/mp4"&gt;
A video showing the demo of using the real-time sticky note bulletin board.
&lt;/video&gt;
&lt;p&gt;You can check out the full code that was created in this post here: &lt;a href="https://github.com/KristofferStrube/DistributedStickyNotes"&gt;https://github.com/KristofferStrube/DistributedStickyNotes&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;You can try out the above demo yourself here: &lt;a href="https://kristofferstrube.github.io/DistributedStickyNotes/"&gt;https://kristofferstrube.github.io/DistributedStickyNotes/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Why Blazor WASM?&lt;/h3&gt;
&lt;p&gt;Before closing off, I promised some people to talk about why I think it makes sense to look at using Blazor WASM together with SignalR event though Blazor Server is already Blazor with SignalR. I think it makes sense to use Blazor WASM when you have rich interactive applications as having a minimal delay for these interactions is essential. Apart from this using SignalR with Blazor WASM also enables us to be much more specific about what we actually send back and forward and we could even deploy limits to this with approaches like throttling. Apart from this, I also like that the Blazor WASM application can live without the connection where it can try to reconnect for a longer time, potentially present other information while reconnecting, and save the current state of the application and re-apply it once reconnected so that no information is lost.&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In this post, we have walked through an example application that utilizes real-time communication. We have explored some approaches to manage concurrent work and interactions. We have looked at how to use typed SignalR hubs and how to use typed SignalR clients to enable end-to-end type safe real-time communication. In the end, we presented a demo of the project and reflected on why it makes sense to use Blazor WASM together with SignalR. We might continue this project in a future blog post touching on scaling using Azure SignalR Service and how to authenticate SignalR connection. If you have any questions or comments for this post then feel free to reach out.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">how-to-send-push-notifications-to-a-browser-in-asp-net-core</guid><link>https://kristoffer-strube.dk/post/how-to-send-push-notifications-to-a-browser-in-asp-net-core/</link><title>How to send push notifications to a browser in ASP.NET Core</title><description>Progressive Web Apps (PWAs) enable a website to make make a lot of interactions that are app-like. Among these are Push Notifications. This is a functionality that enables you to make native notifications for many different devices and to invoke these notifications even when the browser is not active. A lot of websites like the Twitter web app use this functionality with its original intent, but there are of cause also sites that use it maliciously. In this article, we will show how you can subscribe to Push Notifications using ASP.NET Core and how you can send Push Notifications from .NET as well.</description><pubDate>Wed, 22 Mar 2023 00:00:00 +0100</pubDate><a10:updated>2023-03-22T00:00:00+01:00</a10:updated><a10:content type="text">&lt;p&gt;&lt;em&gt;This is a cross-post of this ASP.NET Core MVC-focused post by me: &lt;a href="https://blog.elmah.io/how-to-send-push-notifications-to-a-browser-in-asp-net-core/"&gt;https://blog.elmah.io/how-to-send-push-notifications-to-a-browser-in-asp-net-core/&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;You can find the full code for the project built in this post, in this GitHub Repository: &lt;a href="https://github.com/KristofferStrube/PWAPushNotification"&gt;https://github.com/KristofferStrube/PWAPushNotification&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Setting up a minimal PWA&lt;/h3&gt;
&lt;p&gt;Before we can get started we need to set up a minimal Service Worker and Manifest to meet the minimum requirements for a PWA (Progressive Web App) since we need a PWA for using the Notification API.
We first create the manifest by making a JSON file called &lt;code&gt;manifest.json&lt;/code&gt; which we will place in the &lt;code&gt;wwwroot&lt;/code&gt; folder.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-json"&gt;{
    &amp;quot;name&amp;quot;: &amp;quot;PWA Push Notification&amp;quot;,
    &amp;quot;short_name&amp;quot;: &amp;quot;Push&amp;quot;,
    &amp;quot;icons&amp;quot;: [
        {
            &amp;quot;src&amp;quot;: &amp;quot;/images/icon-48x48.png&amp;quot;,
            &amp;quot;sizes&amp;quot;: &amp;quot;48x48&amp;quot;,
            &amp;quot;type&amp;quot;: &amp;quot;image/png&amp;quot;
        },
        {
            &amp;quot;src&amp;quot;: &amp;quot;/images/icon-512x512.png&amp;quot;,
            &amp;quot;sizes&amp;quot;: &amp;quot;512x512&amp;quot;,
            &amp;quot;type&amp;quot;: &amp;quot;image/png&amp;quot;
        },
        {
            &amp;quot;src&amp;quot;: &amp;quot;/images/icon-192x192.png&amp;quot;,
            &amp;quot;sizes&amp;quot;: &amp;quot;192x192&amp;quot;,
            &amp;quot;type&amp;quot;: &amp;quot;image/png&amp;quot;
        }
    ],
    &amp;quot;start_url&amp;quot;: &amp;quot;/&amp;quot;,
    &amp;quot;display&amp;quot;: &amp;quot;standalone&amp;quot;,
    &amp;quot;background_color&amp;quot;: &amp;quot;#959595&amp;quot;,
    &amp;quot;theme_color&amp;quot;: &amp;quot;#FFFFFF&amp;quot;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Here we define the name for the app, the name that will be displayed in the app if installed on a device, icons for the app in a variety of sizes, the front page URL of the site if the website can be installed as a standalone app, and at the end theme colors. We add a reference to the manifest in the &lt;code&gt;head&lt;/code&gt; of our layout file which is used in all our views &lt;code&gt;&amp;lt;link rel=&amp;quot;manifest&amp;quot; href=&amp;quot;/manifest.json&amp;quot;&amp;gt;&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;We then need to add a Service Worker. We make the minimal Service Worker by creating a JavaScript file &lt;code&gt;ServiceWorker.js&lt;/code&gt;, also in the &lt;code&gt;wwwroot&lt;/code&gt; folder with the following content.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-javascript"&gt;self.addEventListener('fetch', function (event) { });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We reference the service worker by inserting the following script either in a script tag element in your layout file or directly in your main js file if you have such one.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-javascript"&gt;if ('serviceWorker' in navigator) {
    window.addEventListener(&amp;quot;load&amp;quot;, () =&amp;gt; {
        navigator.serviceWorker.register(&amp;quot;/ServiceWorker.js&amp;quot;);
    });
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This code simply checks if your browser supports Service Workers and registers your Service Worker if it does so. Now we have a working Manifest and Service Worker.&lt;/p&gt;
&lt;h3&gt;Subscription and push flow&lt;/h3&gt;
&lt;p&gt;There are 3 primary actors in the subscription/push flow. The web page (includes Service Worker), the push service, and the server. This flow uses a pair of keys, private and public from a standard called VAPID (Voluntary Application Server Identification). There are other ways to secure the communication e.g. FCM (Firebase Cloud Messaging), but VAPID doesn't require you to use any specific platform so we use that. To start we need to make a key pair and save this in our app settings. It's an open standard that uses elliptic curve cryptography and there are a lot of implementations that generate these pairs like the python library &lt;a href="https://pypi.org/project/py-vapid/"&gt;py-vapid&lt;/a&gt;, the node module &lt;a href="https://www.npmjs.com/package/web-push"&gt;web-push&lt;/a&gt; or the online tool &lt;a href="https://tools.reactpwa.com/vapid"&gt;tools.reactpwa.com/vapid&lt;/a&gt;. We will generate a pair and add it to &lt;code&gt;appsettings.json&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-json"&gt;{
    &amp;quot;VAPID&amp;quot;: {
        &amp;quot;subject&amp;quot;: &amp;quot;mailto:mail@example.com&amp;quot;,
        &amp;quot;publicKey&amp;quot;: &amp;quot;BPTnFPVQFAhlIFSWqAjFPtQeEz ... udykg&amp;quot;,
        &amp;quot;privateKey&amp;quot;:  &amp;quot;4jO3OrjQY2ilE ... yuhZWho47Q&amp;quot;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;private key&lt;/code&gt; is never used in other places than on the server and should be kept a secret. The &lt;code&gt;public key&lt;/code&gt; is distributed to the web page and the push service to validate messages. We will send the public key in the next part and the webpage will distribute it to the push service when subscribing. The &lt;code&gt;public key&lt;/code&gt; is used when subscribing so that the push messages from the server can be authenticated because they are signed using the &lt;code&gt;private key&lt;/code&gt;. When the web page subscribes to the push service it gets back an &lt;code&gt;endpoint&lt;/code&gt; which the server will use to send its push message to and two variables &lt;code&gt;p256dh&lt;/code&gt; and &lt;code&gt;auth&lt;/code&gt; that will be used to identify the subscription.&lt;/p&gt;
&lt;h3&gt;Subscribe to Push Notifications&lt;/h3&gt;
&lt;p&gt;We now want to make a view where we go through a few steps to subscribe to Push Notifications. We separate this into 3 states. First, we have a part that we will display if the user has not decided if they will allow Notifications. Then we have a part that we will display either if the browser does not support Notifications or if the user has blocked Notifications. Last, we have a form that we will use to submit an identifier for the user, the &lt;code&gt;endpoint&lt;/code&gt;, and the two subscription identification variables &lt;code&gt;p256dh&lt;/code&gt; and &lt;code&gt;auth&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-html"&gt;&amp;lt;h1&amp;gt;Subscribe to Push Notifications&amp;lt;/h1&amp;gt;
&amp;lt;div id=&amp;quot;GiveAccess&amp;quot; style=&amp;quot;display:none;&amp;quot;&amp;gt;
    Give access to making notifications:
    &amp;lt;button id=&amp;quot;PromptForAccessBtn&amp;quot;&amp;gt;Prompt&amp;lt;/button&amp;gt;
&amp;lt;/div&amp;gt;
&amp;lt;div id=&amp;quot;NoSupport&amp;quot; style=&amp;quot;display:none;&amp;quot;&amp;gt;
    Your browser does not support Push Notifications or you have blocked notifications
&amp;lt;/div&amp;gt;
&amp;lt;form asp-action=&amp;quot;Index&amp;quot; id=&amp;quot;form&amp;quot; style=&amp;quot;display:none;&amp;quot;&amp;gt;
    &amp;lt;label for=&amp;quot;client&amp;quot;&amp;gt;Your name: &amp;lt;/label&amp;gt;
    &amp;lt;input id=&amp;quot;client&amp;quot; name=&amp;quot;client&amp;quot; /&amp;gt;&amp;lt;br /&amp;gt;

    &amp;lt;input id=&amp;quot;endpoint&amp;quot; name=&amp;quot;endpoint&amp;quot; hidden /&amp;gt;
    &amp;lt;input id=&amp;quot;p256dh&amp;quot; name=&amp;quot;p256dh&amp;quot; hidden /&amp;gt;
    &amp;lt;input id=&amp;quot;auth&amp;quot; name=&amp;quot;auth&amp;quot; hidden /&amp;gt;

    &amp;lt;button type=&amp;quot;submit&amp;quot;&amp;gt;Subscribe&amp;lt;/button&amp;gt;
&amp;lt;/form&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We will write a bit JavaScript to control when these different parts are shown and to get the hidden fields for the form.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-html"&gt;@section Scripts {
    &amp;lt;script&amp;gt;
        if ('serviceWorker' in navigator) {
            window.addEventListener(&amp;quot;load&amp;quot;, () =&amp;gt; {
                navigator.serviceWorker.register(&amp;quot;/ServiceWorker.js&amp;quot;)
                    .then((reg) =&amp;gt; {
                        if (Notification.permission === &amp;quot;granted&amp;quot;) {
                            $(&amp;quot;#form&amp;quot;).show();
                            getSubscription(reg);
                        } else if (Notification.permission === &amp;quot;blocked&amp;quot;) {
                            $(&amp;quot;#NoSupport&amp;quot;).show();
                        } else {
                            $(&amp;quot;#GiveAccess&amp;quot;).show();
                            $(&amp;quot;#PromptForAccessBtn&amp;quot;).click(() =&amp;gt; requestNotificationAccess(reg));
                        }
                    });
            });
        } else {
            $(&amp;quot;#NoSupport&amp;quot;).show();
        }

        function requestNotificationAccess(reg) {
            Notification.requestPermission(function (status) {
                $(&amp;quot;#GiveAccess&amp;quot;).hide();
                if (status == &amp;quot;granted&amp;quot;) {
                    $(&amp;quot;#form&amp;quot;).show();
                    getSubscription(reg);
                } else {
                    $(&amp;quot;#NoSupport&amp;quot;).show();
                }
            });
        }

        function getSubscription(reg) {
            reg.pushManager.getSubscription().then(function (sub) {
                if (sub === null) {
                    reg.pushManager.subscribe({
                        userVisibleOnly: true,
                        applicationServerKey: &amp;quot;@ViewBag.applicationServerKey&amp;quot;
                    }).then(function (sub) {
                        fillSubscribeFields(sub);
                    }).catch(function (e) {
                        console.error(&amp;quot;Unable to subscribe to push&amp;quot;, e);
                    });
                } else {
                    fillSubscribeFields(sub);
                }
            });
        }

        function fillSubscribeFields(sub) {
            $(&amp;quot;#endpoint&amp;quot;).val(sub.endpoint);
            $(&amp;quot;#p256dh&amp;quot;).val(arrayBufferToBase64(sub.getKey(&amp;quot;p256dh&amp;quot;)));
            $(&amp;quot;#auth&amp;quot;).val(arrayBufferToBase64(sub.getKey(&amp;quot;auth&amp;quot;)));
        }

        function arrayBufferToBase64(buffer) {
            var binary = '';
            var bytes = new Uint8Array(buffer);
            var len = bytes.byteLength;
            for (var i = 0; i &amp;lt; len; i++) {
                binary += String.fromCharCode(bytes[i]);
            }
            return window.btoa(binary);
        }
    &amp;lt;/script&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;There are quite a lot of functions in this script block. Let's go through them one at a time.&lt;/p&gt;
&lt;p&gt;First, we register our Service worker again. We do this both to check if it can be registered. If it can't then we display the UI-appropriate UI part. We also do it to get the &lt;code&gt;Registration&lt;/code&gt; object for the Service Worker. We then check if the user has given permission to make Notifications. If they granted access then we show the form and call the &lt;code&gt;getSubscription&lt;/code&gt; function which also fills out the hidden fields in the form. If they have blocked notifications then we show the no-support UI part. The third scenario is that the user has not decided yet in which case we will show the UI part for requesting access to make notifications. We also subscribe to the click event for the &lt;code&gt;PromptForAccessBtn&lt;/code&gt; in which case we will invoke the &lt;code&gt;requestNotificationAccess&lt;/code&gt; function. The following is how it looks when the prompt is invoked in Chrome on desktop.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/Prompt.png" alt="Your desktop browser asking for permission" /&gt;&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;requestNotificationAccess&lt;/code&gt; function simply requests permission for making notifications and reacts to the result of the prompt. If they granted the permission then we show the form and call the &lt;code&gt;getSubscription&lt;/code&gt; function once again and if they block we show the no-support UI part.&lt;/p&gt;
&lt;p&gt;In the &lt;code&gt;getSubscription&lt;/code&gt; function we want to get a subscription object from the Service Worker. We first try to get it by calling &lt;code&gt;reg.pushManager.getSubscription&lt;/code&gt;. This method returns a subscription object if there already exists one. If it did return a subscription then we parse that to the &lt;code&gt;fillSubscribeFields&lt;/code&gt; function. If there was no subscription then we try to make one by calling &lt;code&gt;reg.pushManager.subscribe&lt;/code&gt;. We parse the &lt;code&gt;public key&lt;/code&gt; to this function using the razor &lt;code&gt;ViewBag&lt;/code&gt;. To parse this from the controller we simply add the following to our controller and action for this view. In my small example, I just use the &lt;code&gt;HomeController&lt;/code&gt; since this is a small example.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class HomeController : Controller
{
    private readonly IConfiguration configuration;

    public HomeController(IConfiguration configuration)
    {
        this.configuration = configuration;
    }
    public IActionResult Index()
    {
        ViewBag.applicationServerKey = configuration[&amp;quot;VAPID:publicKey&amp;quot;];
        return View();
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Note that we inject &lt;code&gt;IConfiguration&lt;/code&gt; into the controller, but that we have not added it in the &lt;code&gt;startup.cs&lt;/code&gt; file. This can be done because &lt;code&gt;IConfiguration&lt;/code&gt; is automatically dependency injected in ASP.NET. If &lt;code&gt;reg.pushManager.subscribe&lt;/code&gt; returns a subscription then we simply use that to call &lt;code&gt;fillSubscribeFields&lt;/code&gt; now. If something goes wrong and we do not get a subscription then we have no other option than to log an error. This error could occur if the &lt;code&gt;public key&lt;/code&gt; is not generated correctly.&lt;/p&gt;
&lt;p&gt;The &lt;code&gt;fillSubscribeFields&lt;/code&gt; function fills out the hidden inputs in the form. The &lt;code&gt;endpoint&lt;/code&gt; is available as a field in the subscription object. The &lt;code&gt;p256dh&lt;/code&gt; and &lt;code&gt;auth&lt;/code&gt; variables are available through the &lt;code&gt;getKey&lt;/code&gt; function on the object but they are returned as Array Buffers. So we use a function we have made called &lt;code&gt;arrayBufferToBase64&lt;/code&gt; which converts Array Buffers to a base 64 string.&lt;/p&gt;
&lt;h3&gt;Saving the subscription&lt;/h3&gt;
&lt;p&gt;We have made a form that posts the subscription information. We need to define the action that receives this post and save it somehow. We create a new action in our controller.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[HttpPost]
public IActionResult Index(string client, string endpoint, string p256dh, string auth)
{
    if (client == null)
    {
        return BadRequest(&amp;quot;No Client Name parsed.&amp;quot;);
    }
    if (PersistentStorage.GetClientNames().Contains(client))
    {
        return BadRequest(&amp;quot;Client Name already used.&amp;quot;);
    }
    var subscription = new PushSubscription(endpoint, p256dh, auth);
    PersistentStorage.SaveSubscription(client, subscription);
    return View(&amp;quot;Notify&amp;quot;, PersistentStorage.GetClientNames());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We define that this action handles a post by setting the attribute &lt;code&gt;[HttpPost]&lt;/code&gt;. The action takes the same arguments as we have parsed to it from the form. The goal of this action to save the given parameters so that they can be used later. We use a placeholder for any kind of persistent storage which we call &lt;code&gt;PersistentStorage&lt;/code&gt;. This could be any kind of database or even Azure Blob Storage. In the body, we first check if the submitted client name is null in which case we return &lt;code&gt;BadRequest&lt;/code&gt;. This should probably be handled with an error screen of some sort if this was a real scenario. We also check if the client's name is already used in which case we also return &lt;code&gt;BadRequest&lt;/code&gt;. Then if things are looking good we make a new &lt;code&gt;PushSubscription&lt;/code&gt; object which takes all the subscription details as arguments. The &lt;code&gt;PushSubscription&lt;/code&gt; class is from the package &lt;a href="https://github.com/vip30/WebPush-NetCore"&gt;WebPush-NetCore&lt;/a&gt; which handles how to send a notification for us. We then save the &lt;code&gt;PushSubscription&lt;/code&gt; object in our persistent storage. If this were to be saved in a database then there would probably be needed some serialization/deserialization process for this or as an alternative just save each field individually. In the end, we return a new view in which we can make the push notification. We will let anyone have access to this page for demonstration purposes, but this page should probably only be available for administrators in most cases. We will make this action and view after the next section.&lt;/p&gt;
&lt;h3&gt;Event listeners in Service Worker&lt;/h3&gt;
&lt;p&gt;Now, we have made it so that the server has the endpoint and the keys it needs to make a push notification. But before we push a notification we need to make the listeners that handle when a notification is pushed. These are defined in our Service Worker. First, we make the listener for a new push message by adding the following to &lt;code&gt;ServiceWorker.js&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-javascript"&gt;self.addEventListener('push', function (e) {
    var body;

    if (e.data) {
        body = e.data.text();
    } else {
        body = &amp;quot;Standard Message&amp;quot;;
    }

    var options = {
        body: body,
        icon: &amp;quot;images/icon-512x512.png&amp;quot;,
        vibrate: [100, 50, 100],
        data: {
            dateOfArrival: Date.now()
        },
        actions: [
            {
                action: &amp;quot;explore&amp;quot;, title: &amp;quot;Go interact with this!&amp;quot;,
                icon: &amp;quot;images/checkmark.png&amp;quot;
            },
            {
                action: &amp;quot;close&amp;quot;, title: &amp;quot;Ignore&amp;quot;,
                icon: &amp;quot;images/red_x.png&amp;quot;
            },
        ]
    };
    e.waitUntil(
        self.registration.showNotification(&amp;quot;Push Notification&amp;quot;, options)
    );
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The event listener receives a package containing some data. The data is in our case just plain text which we retrieve by calling &lt;code&gt;.text()&lt;/code&gt;. It could have been a JSON object in which case we would call &lt;code&gt;.json()&lt;/code&gt; instead. If it's an empty message then we just default to some standard message. Then comes the primary part of this function, the &lt;code&gt;options&lt;/code&gt; object. This defines what text will be displayed in the notification, the icon that will be shown, how the notification will vibrate (only available on phones), and extra data that some operating systems might use when showing the notification. We can also define different actions in the &lt;code&gt;options&lt;/code&gt; object. We have made two actions. One indicates that something will happen and another closes the notification. We have defined an ID, the text for the action, and an icon for the action for each action. The icon is optional We use the &lt;code&gt;options&lt;/code&gt; object as an argument to the &lt;code&gt;showNotification&lt;/code&gt; which makes the actual notification. A title for the notification is also defined in this function and would normally be the same as the name of your web app as this will be shown with your logo at the top of the notification on most platforms. We have defined two actions, but we have not yet defined what happens when they are used. For this, we define another event listener in the Service Worker.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-javascript"&gt;self.addEventListener('notificationclick', function (e) {
    var notification = e.notification;
    var action = e.action;

    if (action === 'close') {
        notification.close();
    } else {
        // Some actions
        clients.openWindow('http://www.example.com');
        notification.close();
    }
});
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This listener gets a package that contains a reference to the notification and an &lt;code&gt;action&lt;/code&gt; field that contains the ID for the action that was selected. We then close the notification if they press the &lt;code&gt;close&lt;/code&gt; action and do what we want to do else. In this case, we direct the user to a website, but we could also run any other JavaScript code. We could have called an action in our controller and logged the action if we were interested in seeing how many users clicked/closed the notifications.&lt;/p&gt;
&lt;h3&gt;Pushing the notification&lt;/h3&gt;
&lt;p&gt;We have made it to the last part: Sending the push notification. First, we make a new view in which we will pick a client to whom we will push a message. This is the &lt;code&gt;Notify&lt;/code&gt; view that we referenced earlier.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@model List&amp;lt;string&amp;gt;

&amp;lt;h1&amp;gt;Send Push Notifications&amp;lt;/h1&amp;gt;
@if (Model.Count() == 0)
{
    &amp;lt;p&amp;gt;There are no active subscribers&amp;lt;/p&amp;gt;
}
else
{
    &amp;lt;form asp-action=&amp;quot;Notify&amp;quot;&amp;gt;
        &amp;lt;label for=&amp;quot;message&amp;quot;&amp;gt;Message: &amp;lt;/label&amp;gt;
        &amp;lt;input id=&amp;quot;message&amp;quot; name=&amp;quot;message&amp;quot; /&amp;gt;&amp;lt;br /&amp;gt;
        @foreach (string client in Model)
        {
            &amp;lt;input type=&amp;quot;radio&amp;quot; id=&amp;quot;@(client)_identifier&amp;quot; name=&amp;quot;client&amp;quot; value=&amp;quot;@client&amp;quot;&amp;gt;
            &amp;lt;label for=&amp;quot;@(client)_identifier&amp;quot;&amp;gt;@client&amp;lt;/label&amp;gt;&amp;lt;br&amp;gt;
        }
        &amp;lt;button type=&amp;quot;submit&amp;quot;&amp;gt;Push&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The following picture shows the result of this view.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://kristoffer-strube.dk/images/Push.png" alt="Push notification view" /&gt;&lt;/p&gt;
&lt;p&gt;The view uses a list of strings as its model. Each string will represent a client. We parse this list to the view from our action.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public IActionResult Notify()
{
    return View(PersistentStorage.GetClientNames());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We first check if there are no strings in the list and display a fitting message if so. If there are strings in the list, then we are ready to go. We make a form that will post to an action which we also call Notify. In the form, we first make an input for a message that we will send. After that, we make a radio button for each of the clients in the list with corresponding labels. Now we just need to make an Action for the post.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;[HttpPost]
public IActionResult Notify(string message, string client)
{
    if (client == null)
    {
        return BadRequest(&amp;quot;No Client Name parsed.&amp;quot;);
    }
    PushSubscription subscription = PersistentStorage.GetSubscription(client);
    if (subscription == null)
    {
        return BadRequest(&amp;quot;Client was not found&amp;quot;);
    }

    var subject = configuration[&amp;quot;VAPID:subject&amp;quot;];
    var publicKey = configuration[&amp;quot;VAPID:publicKey&amp;quot;];
    var privateKey = configuration[&amp;quot;VAPID:privateKey&amp;quot;];

    var vapidDetails = new VapidDetails(subject, publicKey, privateKey);

    var webPushClient = new WebPushClient();
    try
    {
        webPushClient.SendNotification(subscription, message, vapidDetails);
    }
    catch (Exception exception)
    {
        // Log error
    }

    return View(PersistentStorage.GetClientNames());
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In the action, we first check if a client was selected and if the client exists. Then we extract the &lt;code&gt;public key&lt;/code&gt;, &lt;code&gt;private key&lt;/code&gt;, and our email from our previously injected &lt;code&gt;IConfiguration&lt;/code&gt;. These are passed to the constructor of a new &lt;code&gt;VapidDetails&lt;/code&gt; which is also from the &lt;a href="https://github.com/vip30/WebPush-NetCore"&gt;WebPush-NetCore&lt;/a&gt; package. Then a &lt;code&gt;WebPushClient&lt;/code&gt; is created which can send the Push Notification. The &lt;code&gt;SendNotification&lt;/code&gt; method is called in a try-catch block. This is done because there are a couple of different errors that occur e.g. if the user has unsubscribed from notification or if the service worker has been updated without re-subscribing. In the end, the &lt;code&gt;Notify&lt;/code&gt; view is returned again so that a new notification can be pushed.&lt;/p&gt;
&lt;p&gt;The following video shows the result of this process.&lt;/p&gt;
&lt;iframe width="700" style="aspect-ratio:16/9" src="https://www.youtube.com/embed/aFwTGdL53Mw" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" allowfullscreen&gt;&lt;/iframe&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Now we have set up a minimal Service Worker and Manifest. We have made a view that enables the user to allow notifications and subscribe to Push Notifications. We have made event listeners in the Service Worker which handles when notifications are pushed. In the end, we have pushed a notification from the server using the &lt;a href="https://github.com/vip30/WebPush-NetCore"&gt;WebPush-NetCore&lt;/a&gt; package. There are multiple places where this example could be improved with regards to security and aesthetics, but it still encapsulates the main functionality of Push Notifications and how it works together with ASP.NET Core. If you have feedback or questions, then feel free to reach out and share your thoughts.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">wrapping-compression-streams-in-blazor</guid><link>https://kristoffer-strube.dk/post/wrapping-compression-streams-in-blazor/</link><title>Wrapping Compression Streams in Blazor</title><description>Most modern browsers implement the Compression Streams API. The API defines two interfaces CompressionStream and DecompressionStream which can be used in connection with the browser Streams API to compress and decompress data while it is streamed. This can be useful in scenarios where bandwidth is limited or where huge data blobs are streamed to and from a client. In this article, we will wrap the Compression Streams API in Blazor using definitions from the Blazor.Streams library, and in the end we will make a small Blazor WASM sample that uses it to validate its behavior.</description><pubDate>Fri, 17 Mar 2023 00:00:00 +0100</pubDate><a10:updated>2023-03-17T00:00:00+01:00</a10:updated><a10:content type="text">&lt;p&gt;&lt;em&gt;Disclaimer: &amp;quot;Most modern browsers&amp;quot; means Chrome, Edge, Safari, and Opera. (Note that the list doesn't include Firefox)&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;Setup project&lt;/h3&gt;
&lt;p&gt;The newest version of &lt;a href="https://github.com/KristofferStrube/Blazor.Streams"&gt;Blazor.Streams&lt;/a&gt; uses packages that depend on .NET 7 so we need to download the newest version of .NET 7 from &lt;a href="https://dotnet.microsoft.com/en-us/download/dotnet/7.0"&gt;dotnet.microsoft.com/en-us/download/dotnet/7.0&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;Using the dotnet CLI we can create a new Razor class library in a new folder.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet new razorclasslib
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we add a reference to &lt;a href="https://github.com/KristofferStrube/Blazor.Streams"&gt;Blazor.Streams&lt;/a&gt; using the CLI as well.&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;dotnet add package KristofferStrube.Blazor.Streams
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;razorclasslib&lt;/code&gt; template adds a couple of sample razor components that we don't need, but it also creates a sample JSInterop class which we can change every so little to be our base JS Wrapper class. The JS Wrapper base class should make a couple of fields and properties available: an &lt;code&gt;IJSObjectReference&lt;/code&gt; representing the wrapped object, an &lt;code&gt;IJSRuntime&lt;/code&gt; for invoking JS methods, and a lazily evaluated reference to a helper JS script file.&lt;/p&gt;
&lt;h5&gt;BaseJSWrapper.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public abstract class BaseJSWrapper : IAsyncDisposable
{
    public IJSObjectReference JSReference { get; }
    public IJSRuntime JSRuntime { get; }

    protected readonly Lazy&amp;lt;Task&amp;lt;IJSObjectReference&amp;gt;&amp;gt; helperTask;

    internal BaseJSWrapper(IJSRuntime jSRuntime, IJSObjectReference jSReference)
    {
        helperTask = new(jSRuntime.GetHelperAsync);
        JSReference = jSReference;
        JSRuntime = jSRuntime;
    }

    public async ValueTask DisposeAsync()
    {
        if (helperTask.IsValueCreated)
        {
            IJSObjectReference module = await helperTask.Value;
            await module.DisposeAsync();
        }
        GC.SuppressFinalize(this);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We have an internal constructor as we only want wrapper classes from this project to be able to extend the class.
We initialize the lazy helper task using an &lt;code&gt;IJSRuntime&lt;/code&gt; extension method called &lt;code&gt;GetHelperAsync&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;GetHelperAsync&lt;/code&gt; is defined like so:&lt;/p&gt;
&lt;h5&gt;IJSRuntimeExtensions.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;internal static class IJSRuntimeExtensions
{
    internal static async Task&amp;lt;IJSObjectReference&amp;gt; GetHelperAsync(this IJSRuntime jSRuntime)
    {
        return await jSRuntime.InvokeAsync&amp;lt;IJSObjectReference&amp;gt;(
            &amp;quot;import&amp;quot;,
            &amp;quot;./_content/KristofferStrube.Blazor.CompressionStreams/KristofferStrube.Blazor.CompressionStreams.js&amp;quot;
        );
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The method references our helper JS script file. The first part of the path &lt;code&gt;./content/&lt;/code&gt; is where Blazor stores all resources from packages that it uses. The next part &lt;code&gt;/KristofferStrube.Blazor.CompressionStreams/&lt;/code&gt; is the name of the namespace of our project and after this part of the path we find all the files from the &lt;code&gt;/wwwroot/&lt;/code&gt; folder of our project which is where our helper script is placed. We call the helper script &lt;code&gt;KristofferStrube.Blazor.CompressionStreams.js&lt;/code&gt; so that it is easy to find the specific script from the browser developer tool if necessary which wouldn't be as easy if everyone called their helper scripts &lt;code&gt;helper.js&lt;/code&gt;. We will add some methods to the script file when we need any methods that Blazor doesn't have support for natively.&lt;/p&gt;
&lt;h3&gt;Looking at the WebIDL specification&lt;/h3&gt;
&lt;p&gt;A good start for wrapping any browser API is to look at the WebIDL specification and we are very lucky that the specification for the Compressions Streams API is very short and concise. We can &lt;a href="https://wicg.github.io/compression/#idl-index"&gt;find the specification here&lt;/a&gt; but I have also written it out below as it is very short.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-webidl"&gt;[Exposed=*]
interface CompressionStream {
  constructor(DOMString format);
};
CompressionStream includes GenericTransformStream;

[Exposed=*]
interface DecompressionStream {
  constructor(DOMString format);
};
DecompressionStream includes GenericTransformStream;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We see that it defines two interfaces that are very similar. They are both exposed in &lt;code&gt;*&lt;/code&gt; meaning that they can be constructed in all contexts. We see that they both have a constructor that takes a format. If we scroll up a bit in the specification we find that the API supports 3 different compression algorithms which is what we must parse as the format. It supports the following formats:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;deflate&lt;/code&gt; (ZLIB Compressed Data Format)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;deflate-raw&lt;/code&gt; (The DEFLATE algorithm)&lt;/li&gt;
&lt;li&gt;&lt;code&gt;gzip&lt;/code&gt; (GZIP file format)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;The next thing we note is that the interfaces both include &lt;code&gt;GenericTransformStream&lt;/code&gt;. This means that it inherits the members defined in the &lt;code&gt;GenericTransformStream&lt;/code&gt; mixin interface from the browser Streams API.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-webidl"&gt;interface mixin GenericTransformStream {
  readonly attribute ReadableStream readable;
  readonly attribute WritableStream writable;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We don't have extension properties nor multiple inheritances in C#. But we have defined an interface called &lt;code&gt;IGenericTransformStream&lt;/code&gt; in &lt;a href="https://github.com/KristofferStrube/Blazor.Streams"&gt;Blazor.Streams&lt;/a&gt; that can help us ensure that we implement wrappers for these attributes.&lt;/p&gt;
&lt;h3&gt;Wrapping CompressionStream&lt;/h3&gt;
&lt;p&gt;Let's start by writing the wrapper class for the &lt;code&gt;CompressionStream&lt;/code&gt;.&lt;/p&gt;
&lt;h5&gt;CompressionStream.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class CompressionStream : BaseJSWrapper, IGenericTransformStream
{
    public static Task&amp;lt;CompressionStream&amp;gt; CreateAsync(IJSRuntime jSRuntime, IJSObjectReference jSReference)
    {
        return Task.FromResult(new CompressionStream(jSRuntime, jSReference));
    }

    protected CompressionStream(IJSRuntime jSRuntime, IJSObjectReference jSReference)
        : base(jSRuntime, jSReference) { }

    // Rest of the methods that need to be implemented.
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We make the constructor protected so that consumers of the wrapper class need to use the creator method &lt;code&gt;CreateAsync&lt;/code&gt; to instantiate an instance. This method did not need to return a Task, but for uniformity, we do this as other creator methods might need to do some asynchronous work like registering event listeners or similar through JSInterop. The &lt;code&gt;IGenericTransformStream&lt;/code&gt; interface defines that we need to implement two methods &lt;code&gt;GetReadableAsync&lt;/code&gt; and &lt;code&gt;GetWritableAsync&lt;/code&gt;. So let's do so.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;// Other methods in CompressionStream.cs above here

public async Task&amp;lt;ReadableStream&amp;gt; GetReadableAsync()
{
    IJSObjectReference helper = await helperTask.Value;
    IJSObjectReference jSInstance = await helper.InvokeAsync&amp;lt;IJSObjectReference&amp;gt;(&amp;quot;getAttribute&amp;quot;, JSReference, &amp;quot;readable&amp;quot;);
    return await ReadableStream.CreateAsync(JSRuntime, jSInstance);
}

public async Task&amp;lt;WritableStream&amp;gt; GetWritableAsync()
{
    IJSObjectReference helper = await helperTask.Value;
    IJSObjectReference jSInstance = await helper.InvokeAsync&amp;lt;IJSObjectReference&amp;gt;(&amp;quot;getAttribute&amp;quot;, JSReference, &amp;quot;writable&amp;quot;);
    return await WritableStream.CreateAsync(JSRuntime, jSInstance);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The two methods make it possible to access rich wrapper instances of the &lt;code&gt;readable&lt;/code&gt; and &lt;code&gt;writable&lt;/code&gt; counterparts in the transform stream. In each method, we first await the lazily defined helper. This uses the existing helper &lt;code&gt;IJSObjectReference&lt;/code&gt; for the helper if it has been used before and else creates a new one using the &lt;code&gt;GetHelperAsync&lt;/code&gt; as we defined it in the &lt;code&gt;BaseJSWrapper&lt;/code&gt; constructor. Next, we use the helper to get either the &lt;code&gt;readable&lt;/code&gt; or &lt;code&gt;writable&lt;/code&gt; attribute from our wrapped &lt;code&gt;JSReference&lt;/code&gt; using a helper method called &lt;code&gt;getAttribute&lt;/code&gt;. Then we have an &lt;code&gt;IJSObjectReference&lt;/code&gt; to that attribute which we can create a rich Stream wrapper from. To use the &lt;code&gt;getAttribute&lt;/code&gt; method we need to add it to the helper JS script file.&lt;/p&gt;
&lt;h5&gt;KristofferStrube.Blazor.CompressionStreams.js&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;export function getAttribute(object, attribute) {
    return object[attribute];
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;It is a really simple method, but currently, Blazor doesn't have a method for accessing the attributes of a JS object so we need it. It has been planned for .NET 8 to add methods for this so that we don't need this JS method. You can check out &lt;a href="https://github.com/dotnet/aspnetcore/issues/31151"&gt;the related issue&lt;/a&gt; which I have linked many times before.&lt;/p&gt;
&lt;p&gt;The last thing we need to wrap for the &lt;code&gt;CompressionStream&lt;/code&gt; is its constructor. This will be a second static &lt;code&gt;CreateAsync&lt;/code&gt; method that takes an &lt;code&gt;IJSRuntime&lt;/code&gt; and the &lt;code&gt;format&lt;/code&gt; as it was defined in the WebIDL specification. In the specification, it was defined as being a string, but actually, only 3 different values were possible. So let's create an enum for those options.&lt;/p&gt;
&lt;h5&gt;CompressionAlgorithm.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public enum CompressionAlgorithm
{
    Deflate,
    DeflateRaw,
    Gzip
}

public static class CompressionAlgorithmsExtensions
{
    public static string AsString(this CompressionAlgorithm compressionAlgorithm) =&amp;gt; compressionAlgorithm switch
    {
        CompressionAlgorithm.Deflate =&amp;gt; &amp;quot;deflate&amp;quot;,
        CompressionAlgorithm.DeflateRaw =&amp;gt; &amp;quot;deflate-raw&amp;quot;,
        CompressionAlgorithm.Gzip =&amp;gt; &amp;quot;gzip&amp;quot;,
        _ =&amp;gt; throw new NotSupportedException($&amp;quot;Value {compressionAlgorithm} not supported as a Compression Algorithm format.&amp;quot;),
    };
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We also define an extension function for the enum which maps it to a string. We could have created this mapping using a custom JSON serializer, but I find this more readable and versatile. Now we are ready to make the constructor wrapping method.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;// Other methods in CompressionStream.cs above here

public static async Task&amp;lt;CompressionStream&amp;gt; CreateAsync(IJSRuntime jSRuntime, CompressionAlgorithm format)
{
    IJSObjectReference helper = await jSRuntime.GetHelperAsync();
    IJSObjectReference jSInstance = await helper.InvokeAsync&amp;lt;IJSObjectReference&amp;gt;(&amp;quot;createCompressionStream&amp;quot;, format.AsString());
    return new CompressionStream(jSRuntime, jSInstance);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this method we can't use the lazily evaluated helper as the method is static so we simply just await it directly. Using the helper we invoke a method called &lt;code&gt;createCompressionStream&lt;/code&gt; which calls the constructor with our &lt;code&gt;format&lt;/code&gt;. We then create and return a new &lt;code&gt;CompressionStream&lt;/code&gt; using our protected constructor. So the last part is to add the &lt;code&gt;createCompressionStream&lt;/code&gt; method to our helper JS script file before being done.&lt;/p&gt;
&lt;h5&gt;KristofferStrube.Blazor.CompressionStreams.js&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-js"&gt;// Other helper methods above here

export function createCompressionStream(format) {
    return new CompressionStream(format);
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;This method is also very simple but is necessary as we can't invoke constructors directly from Blazor. This feature is also planned to be added in .NET 8 as a part of the previously linked issue.&lt;/p&gt;
&lt;p&gt;Now we have wrapped &lt;code&gt;CompressionStream&lt;/code&gt; and as we remember &lt;code&gt;DecompressionStream&lt;/code&gt; is very similar so we won't go through that in this article.&lt;/p&gt;
&lt;h3&gt;CompressionStream InProcess&lt;/h3&gt;
&lt;p&gt;Blazor has an InProcess variant of &lt;code&gt;IJSObjectReference&lt;/code&gt; called &lt;code&gt;IJSInProcessObjectReference&lt;/code&gt; which can also make JSInterop calls synchronously. This can be very useful for accessing attributes as we can then use C# properties. &lt;a href="https://github.com/KristofferStrube/Blazor.Streams"&gt;Blazor.Streams&lt;/a&gt; also defines an InProcess interface for &lt;code&gt;IGenericTransformStreamInProcess&lt;/code&gt;. It has the same shape as &lt;code&gt;IGenericTransformStream&lt;/code&gt; but returns InProcess variants of &lt;code&gt;ReadableStream&lt;/code&gt; and &lt;code&gt;WritableStream&lt;/code&gt; called &lt;code&gt;ReadableStreamInProcess&lt;/code&gt; and &lt;code&gt;WritableStreamInProcess&lt;/code&gt;. So the primary benefit of making an InProcess variant of &lt;code&gt;CompressionStream&lt;/code&gt; and &lt;code&gt;DecompressionStream&lt;/code&gt; is its interoperability with the Blazor.Streams package. As we have already seen the process I will just share the result.&lt;/p&gt;
&lt;h5&gt;CompressionStream.InProcess.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public class CompressionStreamInProcess : CompressionStream, IGenericTransformStreamInProcess
{
    public new IJSInProcessObjectReference JSReference { get; set; }

    public static Task&amp;lt;CompressionStreamInProcess&amp;gt; CreateAsync(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference)
        =&amp;gt; Task.FromResult(new CompressionStreamInProcess(jSRuntime, jSReference));

    public new static async Task&amp;lt;CompressionStream&amp;gt; CreateAsync(IJSRuntime jSRuntime, CompressionAlgorithm format)
    {
        IJSObjectReference helper = await jSRuntime.GetHelperAsync();
        IJSInProcessObjectReference jSInstance = await helper.InvokeAsync&amp;lt;IJSInProcessObjectReference&amp;gt;(&amp;quot;createCompressionStream&amp;quot;, format.AsString());
        return await Task.FromResult(new CompressionStreamInProcess(jSRuntime, jSInstance));
    }

    protected CompressionStreamInProcess(IJSRuntime jSRuntime, IJSInProcessObjectReference jSReference) : base(jSRuntime, jSReference)
    {
        JSReference = jSReference;
    }

    public new async Task&amp;lt;ReadableStreamInProcess&amp;gt; GetReadableAsync()
    {
        IJSObjectReference helper = await helperTask.Value;
        IJSInProcessObjectReference jSInstance = await helper.InvokeAsync&amp;lt;IJSInProcessObjectReference&amp;gt;(&amp;quot;getAttribute&amp;quot;, JSReference, &amp;quot;readable&amp;quot;);
        return await ReadableStreamInProcess.CreateAsync(JSRuntime, jSInstance);
    }

    public new async Task&amp;lt;WritableStreamInProcess&amp;gt; GetWritableAsync()
    {
        IJSObjectReference helper = await helperTask.Value;
        IJSInProcessObjectReference jSInstance = await helper.InvokeAsync&amp;lt;IJSInProcessObjectReference&amp;gt;(&amp;quot;getAttribute&amp;quot;, JSReference, &amp;quot;writable&amp;quot;);
        return await WritableStreamInProcess.CreateAsync(JSRuntime, jSInstance);
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The primary difference is that we hide a lot of the members of the &lt;code&gt;CompressionStream&lt;/code&gt; and define InProcess variants instead.&lt;/p&gt;
&lt;p&gt;You can checkout the full implementation at &lt;a href="https://github.com/KristofferStrube/Blazor.CompressionStreams/"&gt;github.com/KristofferStrube/Blazor.CompressionStreams/&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;Validation Sample&lt;/h3&gt;
&lt;p&gt;Now we just need to check that our implementation works. To validate it we will construct a stream, compress it, decompress it, and check that the content is still valid.&lt;/p&gt;
&lt;p&gt;We create a new Blazor WASM project in an empty folder using the CLI once again.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet new blazorwasm
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;From this project, we add a reference to the class library. If you followed the previous steps and made the class library yourself then you can use the &lt;a href="https://learn.microsoft.com/en-us/dotnet/core/tools/dotnet-add-reference"&gt;dotnet add reference&lt;/a&gt; command to reference that project. Else you can add my published NuGet package using this command:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet add package KristofferStrube.Blazor.CompressionStreams
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Once we have added the library reference to our Blazor WASM project we can begin to make our sample page. We will simply modify the pre-generated page &lt;code&gt;Index.razor&lt;/code&gt;. We define the following scaffold for our page:&lt;/p&gt;
&lt;h5&gt;Index.razor&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@page &amp;quot;/&amp;quot;
@using KristofferStrube.Blazor.CompressionStreams

@inject IJSRuntime JSRuntime
@inject HttpClient HttpClient

@if (compressedStreamSize is not 0)
{
    &amp;lt;div&amp;gt;&amp;lt;label style=&amp;quot;width:200px;&amp;quot;&amp;gt;Compressed size was:&amp;lt;/label&amp;gt; @compressedStreamSize&amp;lt;/div&amp;gt;
}
@if (decompressedStreamSize is not 0)
{
    &amp;lt;div&amp;gt;&amp;lt;label style=&amp;quot;width:200px;&amp;quot;&amp;gt;Decompressed size was:&amp;lt;/label&amp;gt; @decompressedStreamSize&amp;lt;/div&amp;gt;
}

&amp;lt;p&amp;gt;
    @content
&amp;lt;/p&amp;gt;

@code {
    string content = &amp;quot;&amp;quot;;
    long compressedStreamSize;
    long decompressedStreamSize;

    protected override async Task OnInitializedAsync()
    {
        // Our compression and decompression code here
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;In this scaffold, we already do a lot. We add an &lt;code&gt;using&lt;/code&gt; for the &lt;code&gt;KristofferStrube.Blazor.CompressionStreams&lt;/code&gt; library and inject the &lt;code&gt;IJSRuntime&lt;/code&gt; and &lt;code&gt;HttpClient&lt;/code&gt; services. We will use the &lt;code&gt;HttpClient&lt;/code&gt; to download some data as our compression guinea pig for compression in a bit. After this, we make some markup that will present the sizes of our stream compressed and decompressed followed by the final content itself. After this, we define these fields in our code section.&lt;/p&gt;
&lt;p&gt;Now we just need to fill out our &lt;code&gt;OnInitializedAsync&lt;/code&gt; method with the actual code for compressing and decompressing a stream. For a start we need some stream of data. For this, we use the aforementioned &lt;code&gt;HttpClient&lt;/code&gt;. We can stream the result of an HTTP request using the &lt;code&gt;GetStreamAsync&lt;/code&gt; method. So we need something to stream and for this purpose, we will use the world-renowned Lorem-Ipsum text which we will copy into a file called &lt;code&gt;lorem.txt&lt;/code&gt; in the &lt;code&gt;/wwwroot/data/&lt;/code&gt; folder of our Blazor WASM project. Then we can fetch it and construct a &lt;code&gt;ReadableStream&lt;/code&gt; from the resulting stream.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var data = await HttpClient.GetStreamAsync(&amp;quot;data/lorem.txt&amp;quot;);
var streamRef = new DotNetStreamReference(stream: data, leaveOpen: false);
var jSStreamReference = await JSRuntime.InvokeAsync&amp;lt;IJSObjectReference&amp;gt;(&amp;quot;jSStreamReference&amp;quot;, streamRef);
var readableStream = await ReadableStream.CreateAsync(JSRuntime, jSStreamReference);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;With ASP.NET Core 6, Blazor got a &lt;code&gt;DotNetStreamReference&lt;/code&gt; type which can be used to construct a JS stream from any .NET &lt;code&gt;Stream&lt;/code&gt; as we do in the above code. From the &lt;code&gt;IJSObjectReference&lt;/code&gt; to this JS stream we construct a &lt;code&gt;ReadableStream&lt;/code&gt; from the &lt;code&gt;Blazor.Streams&lt;/code&gt; library.&lt;/p&gt;
&lt;p&gt;Then we compress the stream using a new instance of a &lt;code&gt;CompressionStream&lt;/code&gt;.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var compressionStream = await CompressionStream.CreateAsync(JSRuntime, CompressionAlgorithm.DeflateRaw);
var compressedStream = await readableStream.PipeThroughAsync(compressionStream);
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;The &lt;code&gt;ReadableStream&lt;/code&gt; wrapper has a method called &lt;code&gt;PipeThroughAsync&lt;/code&gt; which can be used to transform a stream in any way. It takes a class that implements &lt;code&gt;IGenericTransformStream&lt;/code&gt; as its only parameter which we luckily had &lt;code&gt;CompressionStream&lt;/code&gt; implement.&lt;/p&gt;
&lt;p&gt;Now we want to measure the compressed size but reading the compressed stream will consume it. So we first &lt;em&gt;tee&lt;/em&gt; the stream. By teeing the stream we create 2 new identical streams that can be consumed however we want. This also locks the original stream making it impossible to read.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var (tee1, tee2) = await compressedStream.TeeAsync();
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we read the first tee and counts its size.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var reader = await tee1.GetDefaultReaderAsync();
await foreach (var byteArrayChunk in reader.IterateByteArraysAsync())
{
    compressedStreamSize += byteArrayChunk.Length;
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And finally we decompress the second tee and read and measure its size.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;var decompressionStream = await DecompressionStream.CreateAsync(JSRuntime, CompressionAlgorithm.DeflateRaw);
var decompressedStream = await tee2.PipeThroughAsync(decompressionStream);

var writeStream = new System.IO.MemoryStream();
await decompressedStream.CopyToAsync(writeStream);
decompressedStreamSize = writeStream.Length;
content = System.Text.Encoding.UTF8.GetString(writeStream.ToArray());
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we are done. Let's run it and see the result.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;dotnet run
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then we go to the index page. Normally I would have made a video for this, but this sample isn't really that interesting, so I've just copied the result here as what is actually interesting is that the text was compressed and that the resulting content was intact.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-bash"&gt;Compressed size was: 3696
Decompressed size was: 11481
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi et ex a dolor pulvinar euismod... (continuing)
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;You can check out the demo here yourself: &lt;a href="https://kristofferstrube.github.io/Blazor.CompressionStreams/"&gt;https://kristofferstrube.github.io/Blazor.CompressionStreams/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;And you can check out the GitHub project here: &lt;a href="https://github.com/KristofferStrube/Blazor.CompressionStreams"&gt;https://github.com/KristofferStrube/Blazor.CompressionStreams&lt;/a&gt; (Throw me a star if you enjoyed the post.)&lt;/p&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;Now, we have seen an approach for wrapping browser APIs in Blazor WASM. We have wrapped the &lt;code&gt;CompressionStream&lt;/code&gt; interface and the other related interfaces defined in the Compressions Streams API. And in the end, we have shown a small example that validates that the wrapper has the intended behavior. This post is a precursor to a series of posts that I will make on the topic of streaming files to and from Blazor WASM clients using my wrapper classes which I'm looking forward to getting started with. If you have any questions related to the article or comments then feel free to reach out.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">the-blazor-navigationmanager</guid><link>https://kristoffer-strube.dk/post/the-blazor-navigationmanager/</link><title>The Blazor NavigationManager</title><description>The NavigationManager is a service that enables developers to programmatically navigate the user to different pages or external URIs. In ASP.NET Core 7, there were added new features that enable us to parse simple state between pages and listen to and intercept navigation. In this post, we will look at the basic capabilities of the NavigationManager, present what new features were added in ASP.NET Core 7, discuss how it fits into the Blazor/.NET ecosystem, and in the end present a scenario that is now possible with relative ease using new additions from ASP.NET Core 7.</description><pubDate>Thu, 23 Feb 2023 00:00:00 +0100</pubDate><a10:updated>2023-02-23T00:00:00+01:00</a10:updated><a10:content type="text">&lt;h3&gt;Members from before ASP.NET Core 7&lt;/h3&gt;
&lt;p&gt;First, let's see what actions were possible before ASP.NET Core 7. I will give a shallow overview of the commonly used properties and methods of the &lt;code&gt;NavigationManager&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;string BaseUri&lt;/h4&gt;
&lt;p&gt;You can use the &lt;code&gt;BaseUri&lt;/code&gt; property to get or set the base path of where the application is being presented. This is equivalent to the path that the &lt;base /&gt; tag in the head of the HTML document points to, but the path is actually resolved. This means that you get the string &lt;code&gt;&amp;quot;https://example.com/&amp;quot;&lt;/code&gt; if your page is hosted there even though the &lt;code&gt;&amp;lt;base /&amp;gt;&lt;/code&gt; tag might only specify &lt;code&gt;&amp;quot;/&amp;quot;&lt;/code&gt; as the base href.&lt;/p&gt;
&lt;h4&gt;string Uri&lt;/h4&gt;
&lt;p&gt;This is equivalent to the &lt;code&gt;window.location&lt;/code&gt; property in JavaScript which we can use to get and set the absolute URI of the page.&lt;/p&gt;
&lt;h4&gt;void NavigateTo(...)&lt;/h4&gt;
&lt;p&gt;This method is also used to change the location to a specific URI, but this also has multiple useful overloads.&lt;/p&gt;
&lt;h5&gt;void NavigateTo(string uri, bool forceLoad)&lt;/h5&gt;
&lt;p&gt;The &lt;code&gt;forceLoad&lt;/code&gt; parameter defines whether the navigation should simulate an HTTP reload or simply should change to the location without reloading.&lt;/p&gt;
&lt;h5&gt;void NavigateTo(string uri, bool forceLoad = false, bool replace = false)&lt;/h5&gt;
&lt;p&gt;The &lt;code&gt;replace&lt;/code&gt; parameter defines whether the navigation should replace the existing part of the history. This means that you will lose your navigation history i.e. which pages you can navigate to with the back and forward buttons or gestures in the browser if you set it to true which can be desirable in certain scenarios like stateful MVVM-inspired Blazor applications.&lt;/p&gt;
&lt;h5&gt;void NavigateTo(string uri, NavigationOptions options)&lt;/h5&gt;
&lt;p&gt;The &lt;code&gt;options&lt;/code&gt; parameter is simply an option that contains the two properties from the previous constructors as a contained class so that the same options can easily be used in multiple places.&lt;/p&gt;
&lt;h4&gt;string ToAbsoluteUri(String relativeUri)&lt;/h4&gt;
&lt;p&gt;Converts a relative URI to an absolute path in relation to the current path i.e. if you are at &lt;code&gt;&amp;quot;https://example.com/thepage&amp;quot;&lt;/code&gt; and parses &lt;code&gt;&amp;quot;/subpath&amp;quot;&lt;/code&gt; to it then you get &lt;code&gt;https://example.com/thepage/subpath&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;string ToBaseRelativePath(String uri)&lt;/h4&gt;
&lt;p&gt;Converts an absolute URI to a relative in relation path to the base path i.e. if your base path is &lt;code&gt;&amp;quot;https://example.com/mysite&amp;quot;&lt;/code&gt; and you parse &lt;code&gt;&amp;quot;https://example.com/mysite/and/my/many/subpages&amp;quot;&lt;/code&gt; to it then you get &lt;code&gt;&amp;quot;/and/my/many/subpages&amp;quot;&lt;/code&gt;.&lt;/p&gt;
&lt;h4&gt;EventHandler&lt;LocationChangedEventArgs&gt; LocationChanged&lt;/h4&gt;
&lt;p&gt;You can listen to this event which will trigger once a location &lt;u&gt;has changed&lt;/u&gt;. I emphasize that as it means you can't act on the change itself happening. From the event argument, you can see the &lt;code&gt;Location&lt;/code&gt; that the navigation was to and whether this location change was &lt;em&gt;intercepted&lt;/em&gt; (&lt;code&gt;IsNavigationIntercepted&lt;/code&gt;). &lt;em&gt;Intercepted&lt;/em&gt; means that some mechanism has changed the navigation. This is what Blazor does when you press a link on the page to navigate to a specific route instead of loading that page i.e. changing a cross-document navigation to a same-document one.&lt;/p&gt;
&lt;h3&gt;Members introduced in ASP.NET Core 7&lt;/h3&gt;
&lt;p&gt;The following member were the ones added in ASP.NET Core 7 in order to enable more stateful navigation.&lt;/p&gt;
&lt;h4&gt;void RegisterLocationChangingHandler(...)&lt;/h4&gt;
&lt;p&gt;This method is (unlike &lt;code&gt;LocationChanged&lt;/code&gt;) used to handle when the location &lt;u&gt;is changing&lt;/u&gt;. This means that we can actually act on it before potentially navigating/leaving a page. The method takes a &lt;code&gt;Func&lt;/code&gt; from a &lt;code&gt;LocationChangingContext&lt;/code&gt; to a &lt;code&gt;ValueTask&lt;/code&gt;. This means that we can act on the context and do some async work. An example could be the following:&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;NavigationManager.RegisterLocationChangingHandler(async (context) =&amp;gt;
    {
        Console.WriteLine($&amp;quot;We are navigating to: {context.TargetLocation}&amp;quot;);
        await AnimatedTransitionAsync();
    });
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;A nice thing we can do with the context is to stop the navigation using the &lt;code&gt;context.PreventNavigation()&lt;/code&gt; method. This has been a very requested feature. It enables us to support canceling navigation to another page if the user has an unsubmitted form or some state that needs to be saved before leaving the page.&lt;/p&gt;
&lt;h4&gt;string HistoryEntryState&lt;/h4&gt;
&lt;p&gt;Another nice addition from ASP.NET Core 7 is the &lt;code&gt;HistoryEntryState&lt;/code&gt; property. This has been added to many of the types that we have touched on above and enables us to send some state with our navigation without appending a query string (&lt;code&gt;?somekey=somevalue&lt;/code&gt;) or a URI fragment (&lt;code&gt;#someValue&lt;/code&gt;). The property has been added to the &lt;code&gt;NavigationManager&lt;/code&gt; itself, but also to the &lt;code&gt;NavigationOptions&lt;/code&gt;, &lt;code&gt;LocationChangingContext&lt;/code&gt;, and &lt;code&gt;LocationChangedEventArgs&lt;/code&gt; types. Setting the &lt;code&gt;HistoryEntryState&lt;/code&gt; on the &lt;code&gt;NavigationOptions&lt;/code&gt; enables us to append some information that can be acted on by reading the event argument or context in one of the two handlers. The state corresponds to the state that can also be parsed to the &lt;code&gt;navigate&lt;/code&gt; method from the browser Navigation API where the state can be any serializable value. So that we can only parse a string through the &lt;code&gt;HistoryEntryState&lt;/code&gt; is a simplification. But it can be justified as we can just serialize what state we need to parse as JSON or similar and then deserialize it again once read, essentially mimicking what the Navigation API does internally.&lt;/p&gt;
&lt;h3&gt;The &lt;code&gt;NavigationManager&lt;/code&gt; fitting into the Blazor ecosystem&lt;/h3&gt;
&lt;p&gt;When I inspect a &lt;em&gt;new&lt;/em&gt; feature or actually &lt;em&gt;any&lt;/em&gt; feature in Blazor I look at three key parameters for how well I think it fits with the values of Blazor: &lt;strong&gt;Simplicity&lt;/strong&gt;, &lt;strong&gt;Parity&lt;/strong&gt;, and &lt;strong&gt;Familiarity&lt;/strong&gt;.&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Simplicity&lt;/strong&gt; is when we make a simple abstraction over something complex to make the life of the developers easier.&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Parity&lt;/strong&gt; is when we have the same functionality as other web frameworks (or more).&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Familiarity&lt;/strong&gt; is when we have taken known concepts from the .NET ecosystem and made our solution match those.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;If you have all three then you are lucky; If you have two then you have made a deliberate design decision; and if you have one then it is either too simple or you are religious.&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is of cause only a very opinionated analysis.&lt;/em&gt;&lt;/p&gt;
&lt;h4&gt;Simplicity&lt;/h4&gt;
&lt;p&gt;Simplicity is one of the key reasons why someone new to Blazor would actually use a feature as it hides away overly complex functionality. This is done in some parts of the &lt;code&gt;NavigationManager&lt;/code&gt;. One obvious one is the one we have mentioned above related to the &lt;code&gt;HistoryEntryState&lt;/code&gt; that it is simply a &lt;code&gt;string&lt;/code&gt; instead of being some complex object that is annotated as serializable. Another one that is very common in Blazor is the &lt;code&gt;LocationChangedEventArgs&lt;/code&gt; which is simply a Data Transfer Object without any interop functionality. This is what is done for all event arguments across Blazor like &lt;code&gt;MouseEventArgs&lt;/code&gt; or &lt;code&gt;ClipboardEventArgs&lt;/code&gt;. This makes it very simple to work with, but advanced users might be missing more extensibility options.&lt;/p&gt;
&lt;h4&gt;Parity&lt;/h4&gt;
&lt;p&gt;The NavigationManager is obviously meant to be analogous to the browser Navigation API so it makes sense to compare it to the features in that. Below here we see the WebIDL definition of the Navigation interaface from the browser Navigation API.&lt;/p&gt;
&lt;pre&gt;&lt;code class="language-webidl"&gt;interface Navigation : EventTarget {
  sequence&amp;lt;NavigationHistoryEntry&amp;gt; entries();
  readonly attribute NavigationHistoryEntry? currentEntry;
  undefined updateCurrentEntry(NavigationUpdateCurrentEntryOptions options);
  readonly attribute NavigationTransition? transition;

  readonly attribute boolean canGoBack;
  readonly attribute boolean canGoForward;

  NavigationResult navigate(USVString url, optional NavigationNavigateOptions options = {});
  NavigationResult reload(optional NavigationReloadOptions options = {});

  NavigationResult traverseTo(DOMString key, optional NavigationOptions options = {});
  NavigationResult back(optional NavigationOptions options = {});
  NavigationResult forward(optional NavigationOptions options = {});

  attribute EventHandler onnavigate;
  attribute EventHandler onnavigatesuccess;
  attribute EventHandler onnavigateerror;
  attribute EventHandler oncurrententrychange;
};
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;We obviously see some parallels to the &lt;code&gt;NavigationManager&lt;/code&gt; like the &lt;code&gt;currentEntry&lt;/code&gt;, &lt;code&gt;navigate&lt;/code&gt;, &lt;code&gt;onnavigatesuccess&lt;/code&gt;, and &lt;code&gt;oncurrententrychange&lt;/code&gt; members. We also see that there are multiple methods that Blazor has encapsulated by parameterizing the &lt;code&gt;NavigateTo&lt;/code&gt; method i.e. &lt;code&gt;updateCurrentEntry&lt;/code&gt;, &lt;code&gt;navigate&lt;/code&gt;, and &lt;code&gt;reload&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;But we also see parts that are missing. We have two attributes &lt;code&gt;canGoBack&lt;/code&gt; and &lt;code&gt;canGoForward&lt;/code&gt; which would be easy to add like we have access to &lt;code&gt;currentEntry&lt;/code&gt;, but they really only make sense to have if we were also able to call &lt;code&gt;back(...)&lt;/code&gt; and &lt;code&gt;forward(...)&lt;/code&gt;. Apart from this, we are also missing the &lt;code&gt;entries()&lt;/code&gt; and &lt;code&gt;traverseTo(...)&lt;/code&gt; methods that give access to the whole history of navigation in this session and enable us to navigate easily to a specific entry. This is only &lt;strong&gt;one&lt;/strong&gt; part of the whole Navigation API so there are obviously more parts of the API that could have been accessible somehow, be that with direct wrappers or indirect abstractions like &lt;code&gt;NavigateTo&lt;/code&gt; does.&lt;/p&gt;
&lt;h4&gt;Familiarity&lt;/h4&gt;
&lt;p&gt;For many, the key selling point of Blazor is that .NET backend developers can now also develop interactive websites using their knowledge of the .NET ecosystem. For this reason, it makes sense to use concepts from other .NET frameworks when appropriate. If we should relate the &lt;code&gt;NavigationManager&lt;/code&gt; to some mechanism from another .NET framework then I would probably relate it to the &lt;code&gt;WebView2&lt;/code&gt; control which is available in WPF and WinForms applications. This has a lot more methods for navigation and properties giving insight into the current state of the page like &lt;code&gt;GoBack()&lt;/code&gt;, &lt;code&gt;GoForward()&lt;/code&gt;, &lt;code&gt;NavigateToString(...)&lt;/code&gt;, &lt;code&gt;CanGoBack&lt;/code&gt;, &lt;code&gt;ZoomFactor&lt;/code&gt;, and many more! As you can see WebView2 is a lot more featureful, but it is also a wrapper of an actual whole browsing experience so the comparison might not be all that fair as it also has access to things the &lt;code&gt;NavigationManager&lt;/code&gt; couldn't possibly access in its context.&lt;/p&gt;
&lt;p&gt;The naming of the methods or the properties isn't really consistent between the &lt;code&gt;NavigationManager&lt;/code&gt; and the &lt;code&gt;WebView2&lt;/code&gt; control. But a part that is consistent is the usage of &lt;code&gt;EventHandler&lt;/code&gt;s for &lt;code&gt;LocationChanged&lt;/code&gt;/&lt;code&gt;NavigationCompleted&lt;/code&gt;. What doesn't really fit into the style is that &lt;code&gt;WebView2&lt;/code&gt; also uses an &lt;code&gt;EventHander&lt;/code&gt; for when a page is going to change called &lt;code&gt;NavigationStarting&lt;/code&gt; whereas Blazor uses a &lt;code&gt;Func&lt;/code&gt; that is evaluated before navigation by parsing it to the &lt;code&gt;RegisterLocationChangingHandler&lt;/code&gt; as we have seen previously. I'm not quite sure why this distinguishment was made, but my guess would be that this is friendlier to async work whereas &lt;code&gt;event&lt;/code&gt;s inherently has problems when used with async work.&lt;/p&gt;
&lt;h4&gt;Then, how did it do?&lt;/h4&gt;
&lt;p&gt;It did pretty well on the &lt;strong&gt;simplicity&lt;/strong&gt; keeping things easily accessible and encapsulating multiple methods functionality in the &lt;code&gt;NavigateTo&lt;/code&gt; method. It did okay in &lt;strong&gt;parity&lt;/strong&gt; having methods and properties that are clearly mappable to the &lt;code&gt;Navigation&lt;/code&gt; interface from the Navigation API. But there is still missing some functionality. But don't be discouraged we might get these in a future release of ASP.NET if people request this. The &lt;strong&gt;familiarity&lt;/strong&gt; part could need some love and I would especially have liked for all events to use &lt;code&gt;EventHandler&lt;/code&gt;'s and maybe even supply attribute-specific methods as well parallel to &lt;code&gt;addEventListener&lt;/code&gt; and &lt;code&gt;removeEventListener&lt;/code&gt; from the &lt;code&gt;EventTarget&lt;/code&gt; interface which the &lt;code&gt;Navigation&lt;/code&gt; interface extends in the browser specifications.&lt;/p&gt;
&lt;p&gt;So if we were to put it somewhere on the scale that I presented earlier then I would say that the &lt;code&gt;NavigationManager&lt;/code&gt; fulfills somewhere between 1 and 2 of my key parameters for fitting into Blazors values. This puts it somewhere between being too simple and having made deliberate design considerations (which is really good!). And if we would just have had a few more of the obvious missing functionality like &lt;code&gt;Back()&lt;/code&gt; and &lt;code&gt;Forward()&lt;/code&gt; navigation then I would definitely have leaned more towards it fulfilling parity as well. The additions from ASP.NET Core 7 have already pushed us towards this, so we are getting there.&lt;/p&gt;
&lt;h3&gt;Scenario enabled using ASP.NET Core 7&lt;/h3&gt;
&lt;p&gt;I will now show a scenario that is enabled by the additions that came with ASP.NET Core 7. The scenario is &amp;quot;Canceling navigation to prevent loss of data.&amp;quot;
In this scenario, we will make a simple form that can be submitted to an API. We want to display a pop-up if you try to navigate away from the form while there is text in it giving you the option to cancel the navigation. After all the code I have made a small video that shows the scenario in practice.&lt;/p&gt;
&lt;h4&gt;Modal Component&lt;/h4&gt;
&lt;p&gt;Let's first make a simple modal component that can work as a pop-up that forces the user to make a decision.&lt;/p&gt;
&lt;h5&gt;Shared/Modal.razor&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@if (!_isShown) return;

&amp;lt;div class=&amp;quot;modal-container&amp;quot; @onclick=&amp;quot;Close&amp;quot;&amp;gt;
    &amp;lt;div class=&amp;quot;modal-content&amp;quot; @onclick:stopPropagation=true&amp;gt;
        &amp;lt;h3&amp;gt;
            @Title
            &amp;lt;span @onclick=&amp;quot;Close&amp;quot; class=&amp;quot;close&amp;quot;&amp;gt;X&amp;lt;/span&amp;gt;
        &amp;lt;/h3&amp;gt;
        &amp;lt;p&amp;gt;@ChildContent&amp;lt;/p&amp;gt;
        &amp;lt;button @onclick=Accept&amp;gt;Accept&amp;lt;/button&amp;gt;
    &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then the associated code-behind which defines some simple logic for changing whether the modal is visible.&lt;/p&gt;
&lt;h5&gt;Shared/Modal.razor.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public partial class Modal
{
    private bool _isShown { get; set; }
    private Action _accept { get; set; } = default!;

    [Parameter]
    public string Title { get; set; } = &amp;quot;&amp;quot;;
    [Parameter]
    public RenderFragment? ChildContent { get; set; }

    public void Show(Action accept)
    {
        _accept = accept;
        _isShown = true;
        StateHasChanged();
    }

    private void Close()
    {
        _isShown = false;
    }
    private void Accept()
    {
        _accept();
        _isShown = false;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And let's use a little styling to make the modal &lt;em&gt;&amp;quot;pretty&amp;quot;&lt;/em&gt;.&lt;/p&gt;
&lt;h5&gt;Shared/Modal.razor.css&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-css"&gt;.modal-container {
    position: fixed;
    z-index: 1;
    left: 0;
    top: 0;
    width: 100vw;
    height: 100vh;
    background-color: rgba(0, 0, 0, 0.2);
    display: flex;
    justify-content: center;
    align-items: center;
}

.modal-content {
    width: 600px;
    max-width: 80vw;
    padding: 30px;
    background-color: white;
    border-radius: 10px;
}

.close {
    float: right;
}
    .close:hover {
        font-weight: bold;
        cursor: pointer;
    }
&lt;/code&gt;&lt;/pre&gt;
&lt;h4&gt;Index page&lt;/h4&gt;
&lt;p&gt;Next, we will make the markdown for our actual page which will be pretty simple because we encapsulated the modal behavior in its own component.&lt;/p&gt;
&lt;h5&gt;index.razor&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-razor"&gt;@page &amp;quot;/&amp;quot;

&amp;lt;Modal @ref=PreventModal Title=&amp;quot;You are about to leave&amp;quot;&amp;gt;
    You have unsaved data,
    which you will lose if you navigate away from this page.
    Decline to save your data before leaving.
&amp;lt;/Modal&amp;gt;

&amp;lt;label for=&amp;quot;name&amp;quot;&amp;gt;Fill in your name&amp;lt;/label&amp;gt;
&amp;lt;input id=&amp;quot;name&amp;quot; @bind-value=@name&amp;gt;&amp;lt;/input&amp;gt;

&amp;lt;button @onclick=Save&amp;gt;Save!&amp;lt;/button&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;And then the code-behind for the index page. The interesting part is in the &lt;code&gt;OnInitialized&lt;/code&gt; method. It observes all navigation.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;We start off by ignoring cases where the &lt;code&gt;name&lt;/code&gt; field is empty or the parsed &lt;code&gt;HistoryEntryState&lt;/code&gt; had been set to &lt;code&gt;&amp;quot;leave&amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Then we show the &lt;code&gt;Modal&lt;/code&gt; and if they accept that they will leave then we clear the name and navigate to the original &lt;code&gt;TargetLocation&lt;/code&gt; and &lt;code&gt;HistoryEntryState&lt;/code&gt; set to &lt;code&gt;&amp;quot;leave&amp;quot;&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Finally, we prevent the navigation as we want to stay on the site until the user decides.&lt;/li&gt;
&lt;/ol&gt;
&lt;h5&gt;index.razor.cs&lt;/h5&gt;
&lt;pre&gt;&lt;code class="language-csharp"&gt;public partial class Index
{
    private string name = &amp;quot;&amp;quot;;
    private Modal? PreventModal;

    [Inject]
    public NavigationManager NavigationManager { get; set; } = default!;

    protected override void OnInitialized()
    {
        NavigationManager.RegisterLocationChangingHandler((context) =&amp;gt;
        {
            if (string.IsNullOrWhiteSpace(name) || context.HistoryEntryState is &amp;quot;leave&amp;quot;)
            {
                return ValueTask.CompletedTask;
            }

            PreventModal!.Show(accept: () =&amp;gt;
            {
                name = &amp;quot;&amp;quot;;
                NavigationManager.NavigateTo(
                    context.TargetLocation,
                    new NavigationOptions() { HistoryEntryState = &amp;quot;leave&amp;quot; }
                );
            });

            context.PreventNavigation();
            return ValueTask.CompletedTask;
        });
    }

    private void Save()
    {
        Console.WriteLine(&amp;quot;We have saved!&amp;quot;);
        name = &amp;quot;&amp;quot;;
    }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Then we are done with the code part and can enjoy our result below.&lt;/p&gt;
&lt;h4&gt;Demo Video&lt;/h4&gt;
&lt;div class="center"&gt;
&lt;video width="700" autoplay muted controls loop&gt;
&lt;source src="https://kristoffer-strube.dk/videos/navigation-manager.mp4" type="video/mp4"&gt;
A video showing a demo of the code written above.
&lt;/video&gt;
&lt;/div&gt;
&lt;h3&gt;Conclusion&lt;/h3&gt;
&lt;p&gt;In this article, we have looked at what features the &lt;code&gt;NavigationManager&lt;/code&gt; had before ASP.NET Core 7. We have looked at what was introduced in that version. We have discussed the qualities of the &lt;code&gt;NavigationManager&lt;/code&gt; both the good and bad. And in the end, we have presented a small demo scenario with code that showcases some of the new features.&lt;/p&gt;
&lt;p&gt;Feel free to reach out if you have any questions or comments to the post. And remember that parts of this is my subjective opinion.&lt;/p&gt;
</a10:content></item><item><guid isPermaLink="false">welcome-to-the-blog</guid><link>https://kristoffer-strube.dk/post/welcome-to-the-blog/</link><title>Welcome to the blog</title><description>I used to blog for the 4 years that I worked at elmah.io writing a lot about Blazor and .NET; Often in the format of some kind of tutorial or walk-along style article. I stopped my work there towards the end of 2021 as I began focusing on my other work as an instructor at that time. I have been thinking about blogging for others previously, but I feel like there is a lot of loops to get through for those as they of cause need to have a high level of quality. So, in this post I will introduce myself, show some of the projects that I have worked a lot with, and in the end talk about what I will blog about in the future.</description><pubDate>Fri, 17 Feb 2023 00:00:00 +0100</pubDate><a10:updated>2023-02-18T00:00:00+01:00</a10:updated><a10:content type="text">&lt;h3&gt;What do I do?&lt;/h3&gt;
&lt;p&gt;I'm a .NET Developer that posts semi-frequently on &lt;a href="https://twitter.com/KStrubeG"&gt;Twitter&lt;/a&gt; and &lt;a href="https://hachyderm.io/@KristofferStrube"&gt;Mastodon&lt;/a&gt; about the open source projects that I work on, on &lt;a href="https://github.com/KristofferStrube"&gt;GitHub&lt;/a&gt;. I have previously worked professionally with developing/testing Blazor applications as my primary job, but I recently transitioned into a new job that is more heavily architecture-focused. Therefore I work a lot with Blazor in my spare time to get my curiosity satisfied. I graduated in the summer of 2022 with a Master's degree in Computer Science from Aarhus University. So it should be no surprise that I enjoy technically complex problems especially if they have a focus on modeling systems or if they need to work with large amounts of data.&lt;/p&gt;
&lt;h3&gt;Projects&lt;/h3&gt;
&lt;p&gt;The ones of you who know me from Twitter/Mastodon probably do so in the context of two of my projects that I have shared a lot about.&lt;/p&gt;
&lt;p&gt;The first one is my &lt;a href="https://github.com/KristofferStrube/Blazor.FileSystemAccess"&gt;Blazor Wrapper for the File System Access API&lt;/a&gt;. This is a wrapper for a somewhat simple API, but also a very useful API as it enables the consumers of the API to interact with their local file system from the browser. This helps us move towards something a lot of Blazor developers want: Application-like websites. This is not the first wrapper that I have made for Blazor &lt;em&gt;(that is &lt;a href="https://github.com/KristofferStrube/Blazor.Popper"&gt;Blazor.Popper&lt;/a&gt;)&lt;/em&gt;, but it is the project that I have learned the most from. It has also sprouted a handful of other API wrappers that I have worked on to facilitate rich interactions with the API like &lt;a href="https://github.com/KristofferStrube/Blazor.FileSystem"&gt;Blazor.FileSystem&lt;/a&gt;, &lt;a href="https://github.com/KristofferStrube/Blazor.FileAPI"&gt;Blazor.FileAPI&lt;/a&gt;, and &lt;a href="https://github.com/KristofferStrube/Blazor.Streams"&gt;Blazor.Streams&lt;/a&gt;. Through my work with these I have built myself a good standard for how to wrap browser API's in Blazor and have begun to wrap some of the lower level APIs like the DOM API.&lt;/p&gt;
&lt;video width="500" autoplay muted controls loop&gt;
&lt;source src="https://kristoffer-strube.dk/videos/Zip_and_IndexedDB.mp4" type="video/mp4"&gt;
A video showing some of the functionality of the File System Access API wrapper
&lt;/video&gt;
&lt;p&gt;&lt;em&gt;This video is an example of some of the functionality that the File System Access API wrapper supports. This was also one of the first times I had a sample submitted by someone in a PR.&lt;/em&gt;&lt;/p&gt;
&lt;p&gt;The second project some might know me from is my &lt;a href="https://github.com/KristofferStrube/Blazor.SVGEditor"&gt;Blazor SVG Editor&lt;/a&gt;. This is a much more creative and mathematically challenging project that I have worked on for 2 years. With it, I can edit SVGs simply by dragging anchors around the screen and seeing the resulting SVG code in real-time. I often get back to this project when I feel like making something visual or if I really need to be able to make that specific kind of SVG be that a gradient or an animated stroke offset. This also crosses over into my wrapper libraries every so often like when I made a partial wrapper for the SVG Animation API.&lt;/p&gt;
&lt;p&gt;&lt;img src="https://github.com/KristofferStrube/Blazor.SVGEditor/raw/main/docs/showcase.gif?raw=true" alt="The Simpson character Bart being drawn in the SVG Editor." /&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;This is an example of an SVG I created 1½ years ago, which actually landed me my first paid gig outside the borders of Denmark.&lt;/em&gt;&lt;/p&gt;
&lt;h3&gt;This platform&lt;/h3&gt;
&lt;p&gt;When I started writing this I figured this would be the nerdy part, but I see that I have already geeked out a lot, so let's keep it simple. This blog is a static site, generated from markdown files and some configuration in JSON. It is a generator that I have made myself called &lt;a href="http://StaticBlog.NET"&gt;StaticBlog.NET&lt;/a&gt;, but that is not the important part. The important part is that all the content I will write here is in markdown which can be utilized in a huge variety of ways or be consumed by other blogging platforms. This reminds me of a &lt;a href="https://twitter.com/terrajobst/status/1622031997454671872"&gt;comment to a post on Twitter&lt;/a&gt; by &lt;a href="https://twitter.com/terrajobst"&gt;Immo Landwerth&lt;/a&gt;:&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&amp;quot;This is one of the reasons why folks like me decided that we don’t blog straight via WordPress — we use markdown in GitHub and merging there stages it in Wordpress. If they kill that again, we have a bunch of static markdown files we can easily serve.&amp;quot;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;This idea of having a structure that I can easily move is important to me and will be a key focus for me when continuing the development of the generator and editor for this blog.&lt;/p&gt;
&lt;h3&gt;Future posts&lt;/h3&gt;
&lt;p&gt;I intend to make some posts here every so often. At least one post every month. I know that setting goals for creative work can be bad a idea, but I thrive under these kinds of hard measurable goals. Some of my first posts will be about my existing open-source projects, the work I have recently done on them, and the work that follows in the near future.
I have also made a list of some topics that I would like to make some posts about in the near future so that I don't forget:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;.EditorConfig files for .NET and C#&lt;/li&gt;
&lt;li&gt;Performance optimizations in .NET 8&lt;/li&gt;
&lt;li&gt;ActivityPub: The open social network standard&lt;/li&gt;
&lt;li&gt;Concurrency with SignalR and Blazor WASM&lt;/li&gt;
&lt;li&gt;The forgotten Typed SignalR Clients introduced in .NET 7&lt;/li&gt;
&lt;li&gt;StaticBlog.NET: A minimal Static Site Generator written in .NET edited with Blazor WASM.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Feel free to reach out if any of the topics above sounds especially interesting to you. Then I might do them first.&lt;/p&gt;
</a10:content></item></channel></rss>