Jekyll2022-06-08T11:55:41+00:00https://remibou.github.io/feed.xmlRémi Bourgarel’s BlogBlog about software development. Blazor, ASPNET Core, C#, Web Assembly, Spring, Angular, Docker Track Gps With Blazor Browserinterop2020-09-05T00:00:00+00:002020-09-05T00:00:00+00:00https://remibou.github.io/Track-GPS-with-Blazor-BrowserInterop<h1 id="how-to-get-the-user-gps-coordinates-with-blazor-using-browserinterop">How to get the user GPS coordinates with Blazor using BrowserInterop?</h1>
<p>I already said it, Blazor is <a href="https://remibou.github.io/Should-I-peek-Blazor/">awesome</a>. But it got downside. One of these downside comes from the fact that it relies on WebAssembly. WebAssembly doesn’t provide browser API like the <a href="https://developer.mozilla.org/en-US/docs/Web/API/Geolocation_API">Geolocation API</a>. For getting a user GPS coordinates (with his/her approval of course), we need to use JSinterop so we can call the Geolocation API. Because integrating these methods can be tedious I created a package called <a href="https://www.nuget.org/packages/BrowserInterop/">BrowserInterop</a> which implements JSInterop call for many browser API. You can have a look at the repo <a href="https://github.com/RemiBou/BrowserInterop/wiki/Browser-API-covered">WIKI</a> to see the implemented methods.</p>
<p>In this blog post, I will explain how to use this library for getting the user’s geolocation and for knowing when the user moves.</p>
<h2 id="setup">Setup</h2>
<p>First, create a Blazor WASM project (this will also work in Blazor Server side).
In your project folder type :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet new blazorwasm
</code></pre></div></div>
<p>Then reference the BrowserInterop library :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add package BrowserInterop <span class="nt">--version</span> 1.1.1
</code></pre></div></div>
<p>Then on your index.html add a reference to the required js code (after the webassembly.js tag) :</p>
<div class="language-html highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><script </span><span class="na">src=</span><span class="s">"_content/BrowserInterop/scripts.js"</span><span class="nt">></script></span>
</code></pre></div></div>
<p>You can find all the blog post source code <a href="https://github.com/RemiBou/remibou.github.io/tree/master/projects/RemiBou.BlogPost.Gps">here</a>.</p>
<h2 id="getting-the-current-position">Getting the current position</h2>
<p>Now in one of your page, you need the current IJSRuntime :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">@inject</span> <span class="n">IJSRuntime</span> <span class="n">jsRuntime</span>
</code></pre></div></div>
<p>Add a reference to the BrowserInterop namespace that provides the needed methods :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">@using</span> <span class="n">BrowserInterop</span><span class="p">.</span><span class="n">Extensions</span>
<span class="n">@using</span> <span class="n">BrowserInterop</span><span class="p">.</span><span class="n">Geolocation</span>
</code></pre></div></div>
<p>Now get the Geolocation API wrapper :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">@code</span><span class="p">{</span>
<span class="k">private</span> <span class="n">WindowNavigatorGeolocation</span> <span class="n">geolocationWrapper</span><span class="p">;</span>
<span class="k">private</span> <span class="n">GeolocationResult</span> <span class="n">currentPosition</span><span class="p">;</span>
<span class="k">private</span> <span class="n">List</span> <span class="n">positioHistory</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">List</span><span class="p">();</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">OnInitializedAsync</span><span class="p">(){</span>
<span class="kt">var</span> <span class="n">window</span> <span class="p">=</span> <span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">Window</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">navigator</span> <span class="p">=</span> <span class="k">await</span> <span class="n">window</span><span class="p">.</span><span class="nf">Navigator</span><span class="p">();</span>
<span class="n">geolocationWrapper</span> <span class="p">=</span> <span class="n">navigator</span><span class="p">.</span><span class="n">Geolocation</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Note: For improving performance, you could also do it in your Program.cs file, there is no point in doing this for every page load.</p>
<p>Now you can get the current position with this</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="n">button</span> <span class="n">type</span><span class="p">=</span><span class="s">"button"</span> <span class="n">@onclick</span><span class="p">=</span><span class="s">"GetGeolocation"</span><span class="p">></span><span class="n">Get</span> <span class="n">Current</span> <span class="n">Position</span><span class="p"></</span><span class="n">button</span><span class="p">></span>
<span class="nf">@if</span><span class="p">(</span><span class="n">currentPosition</span> <span class="p">!=</span> <span class="k">null</span><span class="p">){</span>
<span class="n">Current</span> <span class="n">position</span> <span class="p">:</span>
<span class="p"><</span><span class="n">ul</span><span class="p">></span>
<span class="n">Latitude</span> <span class="p">:</span> <span class="n">@currentPosition</span><span class="p">.</span><span class="n">Coords</span><span class="p">.</span><span class="n">Latitude</span>
<span class="p"><</span><span class="n">li</span><span class="p">></span><span class="n">Longitude</span> <span class="p">:</span> <span class="n">@currentPosition</span><span class="p">.</span><span class="n">Coords</span><span class="p">.</span><span class="n">Longitude</span> <span class="p"></</span><span class="n">li</span><span class="p">></span>
<span class="p"><</span><span class="n">li</span><span class="p">></span><span class="n">Altitude</span> <span class="p">:</span> <span class="n">@currentPosition</span><span class="p">.</span><span class="n">Coords</span><span class="p">.</span><span class="n">Altitude</span> <span class="p"></</span><span class="n">li</span><span class="p">></span>
<span class="p"><</span><span class="n">li</span><span class="p">></span><span class="n">Accuracy</span> <span class="p">:</span> <span class="n">@currentPosition</span><span class="p">.</span><span class="n">Coords</span><span class="p">.</span><span class="n">Accuracy</span> <span class="p"></</span><span class="n">li</span><span class="p">></span>
<span class="p"><</span><span class="n">li</span><span class="p">></span><span class="n">Altitude</span> <span class="n">Accuracy</span> <span class="p">:</span> <span class="n">@currentPosition</span><span class="p">.</span><span class="n">Coords</span><span class="p">.</span><span class="n">AltitudeAccuracy</span> <span class="p"></</span><span class="n">li</span><span class="p">></span>
<span class="p"><</span><span class="n">li</span><span class="p">></span><span class="n">Heading</span> <span class="p">:</span> <span class="n">@currentPosition</span><span class="p">.</span><span class="n">Coords</span><span class="p">.</span><span class="n">Heading</span> <span class="p"></</span><span class="n">li</span><span class="p">></span>
<span class="p"><</span><span class="n">li</span><span class="p">></span><span class="n">Speed</span> <span class="p">:</span> <span class="n">@currentPosition</span><span class="p">.</span><span class="n">Coords</span><span class="p">.</span><span class="n">Speed</span> <span class="p"></</span><span class="n">li</span><span class="p">></span>
<span class="p"></</span><span class="n">ul</span><span class="p">></span>
<span class="p"></</span><span class="n">div</span><span class="p">></span>
<span class="p">}</span>
<span class="c1">//...</span>
<span class="n">@code</span><span class="p">{</span>
<span class="c1">//...</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">GetGeolocation</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">currentPosition</span> <span class="p">=</span> <span class="p">(</span><span class="k">await</span> <span class="n">geolocationWrapper</span><span class="p">.</span><span class="nf">GetCurrentPosition</span><span class="p">(</span><span class="k">new</span> <span class="nf">PositionOptions</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">EnableHighAccuracy</span> <span class="p">=</span> <span class="k">true</span><span class="p">,</span>
<span class="n">MaximumAgeTimeSpan</span> <span class="p">=</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromHours</span><span class="p">(</span><span class="m">1</span><span class="p">),</span>
<span class="n">TimeoutTimeSpan</span> <span class="p">=</span> <span class="n">TimeSpan</span><span class="p">.</span><span class="nf">FromMinutes</span><span class="p">(</span><span class="m">1</span><span class="p">)</span>
<span class="p">})).</span><span class="n">Location</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">//...</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>If this is the first time, the browser will ask the user for permission and the method will return once the user gave his/her permission.</li>
<li>If the user refuses, Location will be null, and you can get details in the Error property.</li>
<li>All the options and return result are from the browser API, there is no magic here but the JSInterop plumbing.</li>
</ul>
<h2 id="watching-position-change">Watching position change</h2>
<p>Browser also provides an API for watching GPS position change for mobile users. You can watch those changes in your Blazor app with BrowserInterop :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">WatchPosition</span><span class="s">">Watch position
</span><span class="c1">//...</span>
<span class="n">@code</span><span class="p">{</span>
<span class="c1">//...</span>
<span class="k">private</span> <span class="n">List</span> <span class="n">positioHistory</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">List</span><span class="p">();</span>
<span class="k">private</span> <span class="n">IAsyncDisposable</span> <span class="n">geopositionWatcher</span><span class="p">;</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">WatchPosition</span><span class="p">(){</span>
<span class="n">geopositionWatcher</span> <span class="p">=</span> <span class="k">await</span> <span class="n">geolocationWrapper</span><span class="p">.</span><span class="nf">WatchPosition</span><span class="p">(</span><span class="k">async</span> <span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="p">=></span>
<span class="p">{</span>
<span class="n">positioHistory</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">p</span><span class="p">.</span><span class="n">Location</span><span class="p">);</span>
<span class="nf">StateHasChanged</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">);</span>
<span class="p">}</span>
<span class="c1">//...</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>WatchPosition returns an IAsyncDisposable, we’ll see its role later</li>
<li>You need to call StateHasChanged because you are in a callback (StateHasChanged is called automatically at the end of each UI event handler)</li>
<li>The callback should return a ValueTask or be async, here the async will trigger a build warning because there is no await. The purpose of returning Task is if you want to execute async code inside this callback, you can.</li>
</ul>
<p>Now you need to add some code for disposing the watcher or else your component will not be disposed by the GC and your code will still be called after your user went to another page.</p>
<p>First, add the @implements clause on the top of your razor file :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">@implements</span> <span class="n">IAsyncDisposable</span>
</code></pre></div></div>
<p>Then you can add a button for stopping the watcher and dispose it :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p"><</span><span class="n">button</span> <span class="n">type</span><span class="p">=</span><span class="s">"button"</span> <span class="n">@onclick</span><span class="p">=</span><span class="s">"StopWatch"</span><span class="p">></span><span class="n">Stop</span> <span class="n">watch</span><span class="p"></</span><span class="n">button</span><span class="p">></span>
<span class="c1">//...</span>
<span class="n">@code</span><span class="p">{</span>
<span class="c1">//...</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">StopWatch</span><span class="p">(){</span>
<span class="k">await</span> <span class="n">geopositionWatcher</span><span class="p">.</span><span class="nf">DisposeAsync</span><span class="p">();</span>
<span class="n">geopositionWatcher</span> <span class="p">=</span> <span class="k">null</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">ValueTask</span> <span class="nf">DisposeAsync</span><span class="p">(){</span>
<span class="k">await</span> <span class="nf">StopWatch</span><span class="p">();</span>
<span class="p">}</span>
<span class="c1">//...</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>The button is not mandatory but the DisposeAsync is: Blazor will call it when your component is destroyed. This is mandatory like for any event listening in C# (or in any other language that I know of). <strong>This is very important, don’t forget it !!</strong></li>
</ul>
<p>The full source code is available here : <a href="https://github.com/RemiBou/remibou.github.io/tree/master/projects/RemiBou.BlogPost.Gps">https://github.com/RemiBou/remibou.github.io/tree/master/projects/RemiBou.BlogPost.Gps</a></p>
<h2 id="conclusion">Conclusion</h2>
<p>In this blog post, we saw how you can get your user GPS coordinates without writing a single line of JS. I wrote this library so anyone could work with .NET in the browser without knowing a thing about JS. Don’t hesitate to send feedback or ask for more API on the project GitHub page : <a href="https://github.com/RemiBou/BrowserInterop">https://github.com/RemiBou/BrowserInterop</a></p>How to get the user GPS coordinates with Blazor using BrowserInterop?Solid Are Not Rules But Guidelines2020-07-23T00:00:00+00:002020-07-23T00:00:00+00:00https://remibou.github.io/SOLID-are-not-rules-but-guidelines<h1 id="solid-are-not-rules-but-guidelines">SOLID are not rules but guidelines</h1>
<p><em>This article expose my opinion that I got from my own experience and taste please, do not feel offended if you do not agree with it. Off course I might be wrong, it happens everyday.</em></p>
<h2 id="what-is-solid-">What is SOLID ?</h2>
<p><a href="https://en.wikipedia.org/wiki/SOLID#:~:text=In%20object%2Doriented%20computer%20programming,the%20GRASP%20software%20design%20principles.">SOLID</a> are a set of principles for Object Oriented programing that are supposed to help you build great software. By great I mean maintainable, bug free, fast … SOLID is an acronym where every letter is one principle. From my experience, you shouldn’t follow them blindly but think about it, understand it and find where the added value can be. In this article we’ll go through these principles, I’ll try to explain them and the added value I see.</p>
<h3 id="srp">SRP</h3>
<p>SRP stands for Single Responsibility principle, the common explanation is “A class should have only one reason to change”… While it seems like a nice idea : a class with 2k lines is really hard to maintain, what does “one reason to change” mean ? Let’s take this very simple class</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">{</span>
<span class="k">public</span> <span class="kt">int</span> <span class="nf">GetResult</span><span class="p">(){</span>
<span class="k">return</span> <span class="m">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>It has many reason to change</p>
<ul>
<li>We want to take more things into account when we compute the result (add parameters)</li>
<li>We want to change the result , its value or even its type</li>
<li>We want a GetResults that would return the history of result</li>
</ul>
<p>And this is a class with 1 method with 1 line of code. In the SRP definition what bother me is the word “responsibility” its definition is really subjective and vague. I could have a single class of 100k lines of code whose responsibility is “provide online booking for hotel” and a class of 2 methods whose responsibilities are “return the price” and “save the price” and with these 2 responsibilities it wouldn’t respect SRP.</p>
<p>The definition of this principle is so vague that it can’t be called a principle. It’s nearly impossible to explain it to someone, every time the auditor will say “what do you mean by ‘responsibility’ or ‘reason to change’ ?”. If you are an extremist of this rule you might end up with thousand of very little classes and from my experience it’s way worse to maintain than large ones.</p>
<p><strong>What should I keep from SRP ?</strong></p>
<p>SRP means that a class shouldn’t do too many different things. Here are some examples :</p>
<ul>
<li>Manage the state of a Booking and dealing with basic email API => Add a Facade that will enable this class to abstract the email sending. Or better use events so it doesn’t even know some email are sent</li>
<li>Create HTML and SQL queries => create a class for the HTML, one for the SQL query and one that will make them work well together (some sort of MVC)</li>
<li>Authenticate and Authorize => create a class for each role</li>
</ul>
<p>And you can also use metrics of your own to help detect places where you could be breaking SRP :</p>
<ul>
<li>Line of code count</li>
<li>Method count</li>
<li>Field count
…</li>
</ul>
<h2 id="ocp">OCP</h2>
<p>OCP means “Open-Closed Principle”, the usual explanation is “a class should be close to modification but opened to extension”. What this means is that a class behavior should be changed not by changing its code source but by extending it (like implementing the same interface). First this kind of contradict SRP, because now there isn’t a single reason to change a class. And then it’s absolutely useless. Let’s take this class</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">interface</span> <span class="nc">IFoo</span><span class="p">{</span>
<span class="kt">int</span> <span class="nf">GetResult</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Foo</span> <span class="p">:</span> <span class="n">IFoo</span><span class="p">{</span>
<span class="k">public</span> <span class="k">virtual</span> <span class="kt">int</span> <span class="nf">GetResult</span><span class="p">(){</span>
<span class="k">return</span> <span class="m">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>From OCP if I want to change result (like divide it by 2) I would have to implement the decorator pattern like this</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">FooDivideBy2</span> <span class="p">:</span> <span class="n">IFoo</span><span class="p">{</span>
<span class="k">private</span> <span class="n">IFoo</span> <span class="n">decorated</span><span class="p">;</span>
<span class="k">public</span> <span class="nf">FooDivideBy2</span><span class="p">(</span><span class="n">IFoo</span> <span class="n">decorated</span><span class="p">){</span>
<span class="k">this</span><span class="p">.</span><span class="n">decorated</span> <span class="p">=</span> <span class="n">decorated</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">virtual</span> <span class="kt">int</span> <span class="nf">GetResult</span><span class="p">(){</span>
<span class="k">return</span> <span class="n">decorated</span><span class="p">.</span><span class="nf">GetResult</span><span class="p">()</span> <span class="p">/</span> <span class="m">2</span> <span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Instead of 1 class with 5 lines of code, I now have 2 class, one interface and 17 line of code. More than 3 times more line of code, if you apply this principle to larger code base with more interface, more implementation, the impact on maintainability is really important.</p>
<p>One more thing : let’s say I now want to return 2 instead of 1, why would I create a new class when I can change the existing one ?</p>
<p><strong>What should I keep from OCP ?</strong></p>
<p>When you create a library (public or internal to your large company) by definition your class are closed : the user can’t (easily) change their content. So you need to think about it and leave some extension hook so your user can customize your library. In other situation forget about it :)</p>
<h2 id="lsp">LSP</h2>
<p>LSP stands for “Liskov substitution principle”, its usual explanation is “an object can be replaced by any instance of its subtype without altering the correctness of the program”. This means that, from the example from before, we could use FooDivideBy2 or Foo everytime we need an IFoo and the program would still be correct. What I understand is that when you use an interface you shouldn’t worry about the implementation that will be used.</p>
<p>This principle is really nice and going against it adds “leaky abstraction”. This is the case for the IQueryable interface in .NET : when you use it you have to know the implementation your are using because they don’t all behave the same.</p>
<p>I see 2 problems with this principle :</p>
<ul>
<li>Some interface/abstract class are used to provide a common entry point and the interface definition doesn’t say anything about the what it should do, like IObservable</li>
<li>Not all implementation of an interface shares the same edge cases and error. For instance this interface :</li>
</ul>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">IDataSave</span><span class="p">{</span>
<span class="k">void</span> <span class="nf">Save</span><span class="p">(</span><span class="n">Data</span> <span class="n">data</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>If I implement it by saving the data in a json file I will need some specific things done on the Data class (like public getter and setter or parameterless constructor) or it would fail with an IOException if there is some problem on the file system or it would fail because some fields are recursive … These are implementation specific details that the user might need to know.</p>
<p><strong>What should I keep from LSP ?</strong>
You should aim at respecting LSP : the user of the interface should ignore the implementation details and should use the interface the same way for every implementations. But you need to know that in some case it’s impossible or very expensive to do.</p>
<h2 id="isp">ISP</h2>
<p>ISP stands for “Interface Segregation Principle”. Saying it’s better to have many specific interface than one general purpose.</p>
<p>I don’t see any problem with this principle, and I would say that if you end up with this interface</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">interface</span> <span class="nc">IBookingManagement</span><span class="p">{</span>
<span class="k">void</span> <span class="nf">SaveBooking</span><span class="p">(</span><span class="n">Booking</span> <span class="n">aBooking</span><span class="p">);</span>
<span class="k">void</span> <span class="nf">DeleteBooking</span><span class="p">(</span><span class="kt">int</span> <span class="n">aBookingId</span><span class="p">);</span>
<span class="k">void</span> <span class="nf">ValidateBooking</span><span class="p">(</span><span class="kt">int</span> <span class="n">aBookingId</span><span class="p">);</span>
<span class="k">void</span> <span class="nf">DenyBooking</span><span class="p">(</span><span class="kt">int</span> <span class="n">aBookingId</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You didn’t need an interface in the first place ! Interface does not change anything to coupling or code quality, they reduce code readability. So if you want to use some, make sure they bring value. When is that ? I see 2 cases :</p>
<ul>
<li>You have multiple implementations of this interface and the chosen implementation will change at runtime</li>
<li>For testing purpose. I always put things that are out of my control behind an interface so I can mock/fake them for my tests : clock, random, external API, OS dependencies …</li>
</ul>
<p><strong>What should I keep from ISP ?</strong></p>
<p>Almost everything, if one interface has many method and they are not all used in the same context, then you might not need an interface or it’s doing too much and you should split it.</p>
<h2 id="dip">DIP</h2>
<p>DIP stands for “Dependency Inversion Principle”. The idea is there shouldn’t be dependencies between implementations but between an implementation and an abstraction layer (interface).</p>
<p>As I said before interface don’t change a dime in coupling. 2 classes can be decoupled while still having a dependency between the two of them, you just need to make sure they ignore they internal behavior and don’t change how they call each other based on that knowledge. It’s a fallacy to believe that by using the “introduce interface” refactoring you reduce coupling between your classes. Let’s see this example</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">{</span>
<span class="k">private</span> <span class="n">IBar</span> <span class="n">_bar</span><span class="p">;</span><span class="c1">//injected somehow</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">DoStuff</span><span class="p">(){</span>
<span class="n">_bar</span><span class="p">.</span><span class="nf">SaveData</span><span class="p">(</span><span class="s">"a;b;c;d"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">interface</span> <span class="nc">IBar</span><span class="p">{</span>
<span class="k">void</span> <span class="nf">SaveData</span><span class="p">(</span><span class="kt">string</span> <span class="n">str</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Bar</span> <span class="p">:</span> <span class="n">IBar</span><span class="p">{</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">SaveData</span><span class="p">(</span><span class="kt">string</span> <span class="n">str</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">str</span><span class="p">?.</span><span class="n">Length</span> <span class="p">></span> <span class="m">30</span><span class="p">){</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">var</span> <span class="n">data</span> <span class="p">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="sc">';'</span><span class="p">);</span>
<span class="c1">//insert data in DB</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here having an interface doesn’t change anything to the coupling between Foo and Bar : if we change the expected string format in Bar we’ll have to change Foo. What you need to achieve is to reduce coupling by thinking about your class interface (By “interface” I mean all its public methods, not the C# “interface” it implements).</p>
<p><strong>What should I keep from DIP ?</strong></p>
<p>Design your modules so they don’t know how its dependencies works apart from what is specified in the public method signature and documentation.</p>
<h2 id="when-you-drink-the-solid-kool-aid">When you drink the SOLID kool-aid</h2>
<p>I will tell you a bit of my story and I think I’m not the only one with this kind of experience. I was tech lead / architect / mentor / … at a small software editor (under 20 devs at that time) when I started to read about SOLID. And I got into it, I thought it was the answer to all our maintenance problem, spaghetti / lasagna / calzone code base, countless bug etc … So I tried to teach it to the team and following the principles on my own work. But it didn’t change anything :</p>
<ul>
<li>My code was hard to read for the rest of the team</li>
<li>The rest of the team didn’t understand a single thing about the rules and never tried to respect it.</li>
</ul>
<p>From this experience I learned a few things :</p>
<ul>
<li>Only teach pragmatic thing to your team(s). The dogma can help for finding problems but it’s never a solution.</li>
<li>If you find some rules that seems perfect : try to find their limit, make an personal opinion out of it</li>
<li>Never think that sending a link to a good practice article to your team will change anything</li>
<li>Evaluate migration, plan and keep migrating until it’s 100% done. No it won’t work if you apply it “only o new development”. In 5 years if your code base is large enough, 90% will stay untouched yet it still need maintenance/optimization.</li>
<li>When you sell something to your team find some concrete example and propose detailed rules.</li>
</ul>
<h2 id="some-rules-that-i-prefer">Some rules that I prefer</h2>
<p>Now I will tell you about a few rules that I prefer to apply to my own work because they don’t make thing more complicated, they are easier to understand and put in practice (IMO) :</p>
<h3 id="law-of-demeter">Law of Demeter</h3>
<p>The law of Demeter is quite easy to explain : in a method Do in a class Foo you can only do the following :</p>
<ul>
<li>change state of Foo (private fields)</li>
<li>call a method on any parameter of Do</li>
<li>call a method on any variable instantiated inside Do</li>
<li>call a method on any fields of Foo</li>
</ul>
<p>To apply this in C# it’s quite easy : stop making properties setter public !</p>
<p>Instead of this :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">{</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">Do</span><span class="p">(</span><span class="n">Bar</span> <span class="n">b</span><span class="p">){</span>
<span class="n">b</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Bar</span><span class="p">{</span>
<span class="k">public</span> <span class="kt">bool</span> <span class="n">Enabled</span><span class="p">{</span><span class="k">get</span><span class="p">;</span><span class="k">set</span><span class="p">;}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Do this :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">{</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">Do</span><span class="p">(</span><span class="n">Bar</span> <span class="n">b</span><span class="p">){</span>
<span class="n">b</span><span class="p">.</span><span class="nf">Enable</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Bar</span><span class="p">{</span>
<span class="c1">//the coule be fully private</span>
<span class="k">public</span> <span class="kt">bool</span> <span class="n">Enabled</span><span class="p">{</span><span class="k">get</span><span class="p">;</span> <span class="k">private</span> <span class="k">set</span><span class="p">;}</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">Enable</span><span class="p">(){</span>
<span class="k">this</span><span class="p">.</span><span class="n">Enabled</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Like this you are doing beautiful OOP : each object is responsible for its state. Class are not only data holder, they also bring behavior. And you will see with time that it’s easier to maintain as everything will be more explicit : in the first example the verb “Enable” was never written. Here it’s a simple example, but having every state change expressed as a verb make thing clearer and more obvious.</p>
<h3 id="yagni">YAGNI</h3>
<p>YAGNI stands for “You Ain’t Gonna Need It”. What this means is that you should only do the work you are asked to do. Don’t lay the ground for some future imaginary changes, or even code hidden feature so you can feel like a hero saying “it’s already done” in a meeting.</p>
<p>Let’s take an example : you are asked to create an CRUD app that will save data on MSSQL. What you do is you create 5 projects in Visual Studio, one for UI, one for Business, one for DAO and 2 for abstractions between each layer. Settings up the project, resolving dependencies, mapping object between layers … cost you like 40% of the project. This work can be valuable in an imaginary future indeed but you paid for it and you are not sure it’ll be useful.</p>
<p>It’s like choosing to buy a family car when you are in your 20s because you think you will have kids. What will happen ? You might have kids when you will be 30 and by that time your car will be obsolete or broken, so you buy a new one.</p>
<p>You can prepare for future evolution only if it doesn’t increase your project cost. You. don’t. know. the. future.</p>
<h3 id="make-the-implicit-explicit">Make the implicit explicit</h3>
<p>This is a rule I love to repeat in my head when coding. Let’s say you have 2 piece of code</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">{</span>
<span class="c1">// return -1 if value is not valid </span>
<span class="k">public</span> <span class="kt">int</span> <span class="nf">Validate</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">id</span> <span class="p">></span> <span class="m">10000</span><span class="p">)</span>
<span class="k">return</span> <span class="p">-</span><span class="m">1</span><span class="p">;</span>
<span class="k">return</span> <span class="m">0</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>A caller must read the documentation and then compare the result to -1 to see if the value is correct or not. This is not complicated but still I prefer to do it like this</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">{</span>
<span class="k">public</span> <span class="kt">bool</span> <span class="nf">IsValid</span><span class="p">(</span><span class="kt">int</span> <span class="n">id</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">id</span> <span class="p">></span> <span class="m">10000</span><span class="p">)</span>
<span class="k">return</span> <span class="k">false</span><span class="p">;</span>
<span class="k">return</span> <span class="k">true</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>We don’t need any documentation, the behavior is explicit, we’ll integrate this method way faster than the previous one.</p>
<p>A good sign is indeed documentation : if you need to read a method documentation it might not be explicit enough. If you can’t make it more explicit without impacting readability (like changing to a 200 char long method name) then add comment.</p>
<h3 id="kiss">KISS</h3>
<p>KISS stand for “Keep It Simple Stupid”. It’s not a rule and it’s very subjective, so it’s easy to have argument with a colleague about whether something is simple or not. But for me, KISS also means “Don’t follow rules, follow the value”, let’s copy and example for a previous paragraph :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">Foo</span><span class="p">{</span>
<span class="k">private</span> <span class="n">IBar</span> <span class="n">_bar</span><span class="p">;</span><span class="c1">//injected somehow</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">DoStuff</span><span class="p">(){</span>
<span class="n">_bar</span><span class="p">.</span><span class="nf">SaveData</span><span class="p">(</span><span class="s">"a;b;c;d"</span><span class="p">)</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">interface</span> <span class="nc">IBar</span><span class="p">{</span>
<span class="k">void</span> <span class="nf">SaveData</span><span class="p">(</span><span class="kt">string</span> <span class="n">str</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">Bar</span> <span class="p">:</span> <span class="n">IBar</span><span class="p">{</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">SaveData</span><span class="p">(</span><span class="kt">string</span> <span class="n">str</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="n">str</span><span class="p">?.</span><span class="n">Length</span> <span class="p">></span> <span class="m">30</span><span class="p">){</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">InvalidOperationException</span><span class="p">(</span><span class="n">str</span><span class="p">);</span>
<span class="p">}</span>
<span class="kt">var</span> <span class="n">data</span> <span class="p">=</span> <span class="n">str</span><span class="p">.</span><span class="nf">Split</span><span class="p">(</span><span class="sc">';'</span><span class="p">);</span>
<span class="c1">//insert data in DB</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>What is the value of IBar if I have only one implementation ? None. But it costs a lot :</p>
<ul>
<li>Every single time I will navigate between Foo and Bar, I will end up on the interface</li>
<li>Every time I want to add a parameter to SaveData I will have to change the class and the interface</li>
<li>I will have to setup DI for injecting Bar
…</li>
</ul>
<p>If there is no added value, remove the superflux. If it’s simple to insert data directly in your controller DO IT, ignore SOLID, ignore everything, do what seems to be simple.</p>
<p>It’s the same for layered architecture, for each layer you need to evaluate the cost in complexity against its added value. Don’t add layer because someone said so.</p>
<h3 id="stop-renaming-shit-srs-patent-pending">Stop Renaming Shit (SRS, patent pending)</h3>
<p>This is a rule of mine : even if he first naming was shit, keep it or rename it everywhere. It’s really hard to follow a code base and see a concept like “product model” becoming “product type” on a layer which then becomes “item kind” on the next layer and then ends up “buyable thing category”. Every developer has an opinion on how to name thing, don’t be presumptuous, you are not better than your colleague, keep its choice it’ll be better for both of you.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Our job is difficult as we have a lot of complexity to apprehend and sometimes some rules seems like the magic solution to all our problem : they are not and never will be.</p>
<p>I hope I was able to explain my opinion clearly in this post, don’t hesitate to question my skills in the comment :)</p>SOLID are not rules but guidelines This article expose my opinion that I got from my own experience and taste please, do not feel offended if you do not agree with it. Off course I might be wrong, it happens everyday.Realtime Update With Blazor Wasm Signalr And Mediatr2020-05-24T00:00:00+00:002020-05-24T00:00:00+00:00https://remibou.github.io/Realtime-update-with-Blazor-WASM-SignalR-and-MediatR<h1 id="how-to-have-realtime-update-in-your-app-with-blazor-wasm-signalr-and-mediatr">How to have realtime update in your app with Blazor WASM, SignalR and MediatR</h1>
<p>One of the big problem with web app is real time update : most of the traffic is from the client to the server, so the server cannot say anything to the client like “there is a new message for you”. Fortunately, there is many technical solution for this : server-send events, long polling, web socket … In the .net world we are very lucky because there is one project that helps abstracting over which solution to choose (some of them are available only in some web browser or only in some web server) : <a href="https://dotnet.microsoft.com/apps/aspnet/signalr">SignalR</a>.</p>
<p>I will explain in this blog post how to implement live reloading of a Blazor WASM GUI with SignalR and MediatR (my other prefered OSS project).</p>
<h2 id="setup">Setup</h2>
<p>We’ll start with a fresh project (you can checkout the project <a href="https://github.com/RemiBou/remibou.github.io/tree/master/projects/RemiBou.BlogPosts.SignalR">here</a>)</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet new blazorwasm <span class="nt">-ho</span> <span class="nt">-o</span> <span class="nb">.</span>
</code></pre></div></div>
<p>This creates a Blazor WASM project hosted by an ASPNET Core backend.</p>
<p>Now add SignalR/MediatR dependency to your client (Blazor WASM) project</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add Client package Microsoft.AspNetCore.SignalR.Client
dotnet add MediatR
</code></pre></div></div>
<p>And only MediatR to the server because SignalR is included by default (they change ASPNET dependencies every week maybe it’s different in your country)</p>
<div class="language-sh highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add package MediatR
</code></pre></div></div>
<h2 id="signalr-setup-server-side">SignalR setup server-side</h2>
<p>SignalR works with “hub” : the server creates a hub, the client subscribes to it and then the server pushes messages to the hub. The first step is to create a hub on the server :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">public</span> <span class="k">class</span> <span class="nc">HubNotificationHandler</span> <span class="p">:</span> <span class="n">Hub</span>
<span class="p">{</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now you need to wire SignalR with ASPNET Core middleware, in Startup.cs add the service</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">void</span> <span class="nf">ConfigureServices</span><span class="p">(</span><span class="n">IServiceCollection</span> <span class="n">services</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">services</span><span class="p">.</span><span class="nf">AddControllersWithViews</span><span class="p">();</span>
<span class="n">services</span><span class="p">.</span><span class="nf">AddRazorPages</span><span class="p">();</span>
<span class="n">services</span><span class="p">.</span><span class="nf">AddSignalR</span><span class="p">();</span>
<span class="n">services</span><span class="p">.</span><span class="nf">AddMediatR</span><span class="p">(</span><span class="k">this</span><span class="p">.</span><span class="nf">GetType</span><span class="p">().</span><span class="n">Assembly</span><span class="p">);</span>
<span class="n">services</span><span class="p">.</span><span class="nf">AddResponseCompression</span><span class="p">(</span><span class="n">opts</span> <span class="p">=></span>
<span class="p">{</span>
<span class="n">opts</span><span class="p">.</span><span class="n">MimeTypes</span> <span class="p">=</span> <span class="n">ResponseCompressionDefaults</span><span class="p">.</span><span class="n">MimeTypes</span><span class="p">.</span><span class="nf">Concat</span><span class="p">(</span>
<span class="k">new</span><span class="p">[]</span> <span class="p">{</span> <span class="s">"application/octet-stream"</span> <span class="p">});</span>
<span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And add the endpoint that wire incoming subscription to the hub we created</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">void</span> <span class="nf">Configure</span><span class="p">(</span><span class="n">IApplicationBuilder</span> <span class="n">app</span><span class="p">,</span> <span class="n">IWebHostEnvironment</span> <span class="n">env</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">app</span><span class="p">.</span><span class="nf">UseEndpoints</span><span class="p">(</span><span class="n">endpoints</span> <span class="p">=></span>
<span class="p">{</span>
<span class="n">endpoints</span><span class="p">.</span><span class="n">MapHub</span><span class="p"><</span><span class="n">NotificationHub</span><span class="p">>(</span><span class="s">"/notifications"</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Let’s say I have this server-side MediatR notification :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="p">[</span><span class="n">ApiController</span><span class="p">]</span>
<span class="p">[</span><span class="nf">Route</span><span class="p">(</span><span class="s">"[controller]"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">HomeController</span> <span class="p">:</span> <span class="n">ControllerBase</span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">static</span> <span class="kt">int</span> <span class="n">Counter</span> <span class="p">=</span> <span class="m">0</span><span class="p">;</span>
<span class="k">private</span> <span class="n">MediatR</span><span class="p">.</span><span class="n">IMediator</span> <span class="n">_mediator</span><span class="p">;</span>
<span class="k">public</span> <span class="nf">HomeController</span><span class="p">(</span><span class="n">IMediator</span> <span class="n">mediator</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_mediator</span> <span class="p">=</span> <span class="n">mediator</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">[</span><span class="nf">HttpPost</span><span class="p">(</span><span class="s">"increment"</span><span class="p">)]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Post</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">int</span> <span class="n">val</span> <span class="p">=</span> <span class="n">Interlocked</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="k">ref</span> <span class="n">Counter</span><span class="p">);</span>
<span class="k">await</span> <span class="n">_mediator</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="k">new</span> <span class="nf">CounterIncremented</span><span class="p">(</span><span class="n">val</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You can trigger it with a curl call</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>curl <span class="nt">-X</span> POST <span class="nt">-d</span> <span class="s2">""</span> http://localhost:5000/home/increment
</code></pre></div></div>
<h2 id="sending-notifications-from-server-to-the-client">Sending notifications from server to the client</h2>
<p>What I want to do is this : when some notifications are sent in MediatR: send them on the Hub. Then on the client receive those notification and pass them to the component that subscribed to it.</p>
<p>First I need to send notifications to the hub, so I change my hub like this</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">HubNotificationHandler</span> <span class="p">:</span> <span class="n">INotificationHandler</span><span class="p"><</span><span class="n">CounterIncremented</span><span class="p">></span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">readonly</span> <span class="n">IHubContext</span><span class="p"><</span><span class="n">NotificationHub</span><span class="p">></span> <span class="n">_hubContext</span><span class="p">;</span>
<span class="k">public</span> <span class="nf">HubNotificationHandler</span><span class="p">(</span><span class="n">IHubContext</span><span class="p"><</span><span class="n">NotificationHub</span><span class="p">></span> <span class="n">hubContext</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">_hubContext</span> <span class="p">=</span> <span class="n">hubContext</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Handle</span><span class="p">(</span><span class="n">CounterIncremented</span> <span class="n">notification</span><span class="p">,</span> <span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">await</span> <span class="nf">SendNotification</span><span class="p">(</span><span class="n">notification</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">SendNotification</span><span class="p">(</span><span class="n">SerializedNotification</span> <span class="n">notification</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">_hubContext</span><span class="p">.</span><span class="n">Clients</span><span class="p">.</span><span class="n">All</span><span class="p">.</span><span class="nf">SendAsync</span><span class="p">(</span><span class="s">"Notification"</span><span class="p">,</span> <span class="n">notification</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>I will need to implement INotificationHandler for every kind of notification that I want to send to the client. We cannot do a wildcard handler in MediatR but here it’s a good thing : I want to tell explicitely which kind of event are sent.</li>
</ul>
<p>I need a custom Json converter for handling polymorphic serialization and deserialization of my event. Here is its definition</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">abstract</span> <span class="k">class</span> <span class="nc">SerializedNotification</span> <span class="p">:</span> <span class="n">INotification</span>
<span class="p">{</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">NotificationType</span>
<span class="p">{</span>
<span class="k">get</span>
<span class="p">{</span>
<span class="k">return</span> <span class="k">this</span><span class="p">.</span><span class="nf">GetType</span><span class="p">().</span><span class="n">Name</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">set</span><span class="p">{}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">NotificationJsonConverter</span> <span class="p">:</span> <span class="n">JsonConverter</span><span class="p"><</span><span class="n">SerializedNotification</span><span class="p">></span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">readonly</span> <span class="n">IEnumerable</span><span class="p"><</span><span class="n">Type</span><span class="p">></span> <span class="n">_types</span><span class="p">;</span>
<span class="k">public</span> <span class="nf">NotificationJsonConverter</span><span class="p">()</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">type</span> <span class="p">=</span> <span class="k">typeof</span><span class="p">(</span><span class="n">SerializedNotification</span><span class="p">);</span>
<span class="n">_types</span> <span class="p">=</span> <span class="n">AppDomain</span><span class="p">.</span><span class="n">CurrentDomain</span><span class="p">.</span><span class="nf">GetAssemblies</span><span class="p">()</span>
<span class="p">.</span><span class="nf">SelectMany</span><span class="p">(</span><span class="n">s</span> <span class="p">=></span> <span class="n">s</span><span class="p">.</span><span class="nf">GetTypes</span><span class="p">())</span>
<span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">p</span> <span class="p">=></span> <span class="n">type</span><span class="p">.</span><span class="nf">IsAssignableFrom</span><span class="p">(</span><span class="n">p</span><span class="p">)</span> <span class="p">&&</span> <span class="n">p</span><span class="p">.</span><span class="n">IsClass</span> <span class="p">&&</span> <span class="p">!</span><span class="n">p</span><span class="p">.</span><span class="n">IsAbstract</span><span class="p">)</span>
<span class="p">.</span><span class="nf">ToList</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="n">SerializedNotification</span> <span class="nf">Read</span><span class="p">(</span><span class="k">ref</span> <span class="n">Utf8JsonReader</span> <span class="n">reader</span><span class="p">,</span> <span class="n">Type</span> <span class="n">typeToConvert</span><span class="p">,</span> <span class="n">JsonSerializerOptions</span> <span class="n">options</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">reader</span><span class="p">.</span><span class="n">TokenType</span> <span class="p">!=</span> <span class="n">JsonTokenType</span><span class="p">.</span><span class="n">StartObject</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">JsonException</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">using</span> <span class="p">(</span><span class="kt">var</span> <span class="n">jsonDocument</span> <span class="p">=</span> <span class="n">JsonDocument</span><span class="p">.</span><span class="nf">ParseValue</span><span class="p">(</span><span class="k">ref</span> <span class="n">reader</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(!</span><span class="n">jsonDocument</span><span class="p">.</span><span class="n">RootElement</span><span class="p">.</span><span class="nf">TryGetProperty</span><span class="p">(</span><span class="s">"notificationType"</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">typeProperty</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">JsonException</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">var</span> <span class="n">type</span> <span class="p">=</span> <span class="n">_types</span><span class="p">.</span><span class="nf">FirstOrDefault</span><span class="p">(</span><span class="n">x</span> <span class="p">=></span> <span class="n">x</span><span class="p">.</span><span class="n">Name</span> <span class="p">==</span> <span class="n">typeProperty</span><span class="p">.</span><span class="nf">GetString</span><span class="p">());</span>
<span class="k">if</span> <span class="p">(</span><span class="n">type</span> <span class="p">==</span> <span class="k">null</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nf">JsonException</span><span class="p">();</span>
<span class="p">}</span>
<span class="kt">var</span> <span class="n">jsonObject</span> <span class="p">=</span> <span class="n">jsonDocument</span><span class="p">.</span><span class="n">RootElement</span><span class="p">.</span><span class="nf">GetRawText</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">result</span> <span class="p">=</span> <span class="p">(</span><span class="n">SerializedNotification</span><span class="p">)</span><span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Deserialize</span><span class="p">(</span><span class="n">jsonObject</span><span class="p">,</span> <span class="n">type</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>
<span class="k">return</span> <span class="n">result</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">Write</span><span class="p">(</span><span class="n">Utf8JsonWriter</span> <span class="n">writer</span><span class="p">,</span> <span class="n">SerializedNotification</span> <span class="k">value</span><span class="p">,</span> <span class="n">JsonSerializerOptions</span> <span class="n">options</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">JsonSerializer</span><span class="p">.</span><span class="nf">Serialize</span><span class="p">(</span><span class="n">writer</span><span class="p">,</span> <span class="p">(</span><span class="kt">object</span><span class="p">)</span><span class="k">value</span><span class="p">,</span> <span class="n">options</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>Perf are not the best, the GetRawText(à is not a good idea ad it can allocate a lot of memory. If you work at StackOverflow you’ll find someone to help you about that.</li>
<li>This converter is actually simple : it adds a field to the JSON with the child type and reads it when deserializaing. There isn’t (AFAIK) any security issues because the child type list is finite and known by the developper.</li>
</ul>
<p>Here is how I wire my custom converter to SignalR protocol (in Startup.cs)</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddSignalR</span><span class="p">()</span>
<span class="p">.</span><span class="nf">AddJsonProtocol</span><span class="p">(</span><span class="n">o</span> <span class="p">=></span> <span class="n">o</span><span class="p">.</span><span class="n">PayloadSerializerOptions</span><span class="p">.</span><span class="n">Converters</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">NotificationJsonConverter</span><span class="p">()));</span>
</code></pre></div></div>
<p>On the client side you need to listen to event. Because Microsoft DI doesn’t support runtime dynamic configuration (once you called app.Build() you can’t change anything) I can’t use MediatR for sending the notification to the components. So I need to write some kind of service locator for finding the components that implements INotificationHandler. To do this I created this class</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">public</span> <span class="k">static</span> <span class="k">class</span> <span class="nc">DynamicNotificationHandlers</span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">static</span> <span class="n">Dictionary</span><span class="p"><</span><span class="n">Type</span><span class="p">,</span> <span class="n">List</span><span class="p"><(</span><span class="kt">object</span><span class="p">,</span><span class="n">Func</span><span class="p"><</span><span class="n">SerializedNotification</span><span class="p">,</span> <span class="n">Task</span><span class="p">>)>></span> <span class="n">_handlers</span> <span class="p">=</span> <span class="k">new</span> <span class="n">Dictionary</span><span class="p"><</span><span class="n">Type</span><span class="p">,</span> <span class="n">List</span><span class="p"><(</span><span class="kt">object</span><span class="p">,</span> <span class="n">Func</span><span class="p"><</span><span class="n">SerializedNotification</span><span class="p">,</span> <span class="n">Task</span><span class="p">>)>>();</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="n">Register</span><span class="p"><</span><span class="n">T</span><span class="p">>(</span><span class="n">INotificationHandler</span><span class="p"><</span><span class="n">T</span><span class="p">></span> <span class="n">handler</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="n">SerializedNotification</span>
<span class="p">{</span>
<span class="k">lock</span> <span class="p">(</span><span class="n">_handlers</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">handlerInterfaces</span> <span class="p">=</span> <span class="n">handler</span>
<span class="p">.</span><span class="nf">GetType</span><span class="p">()</span>
<span class="p">.</span><span class="nf">GetInterfaces</span><span class="p">()</span>
<span class="p">.</span><span class="nf">Where</span><span class="p">(</span><span class="n">x</span> <span class="p">=></span>
<span class="n">x</span><span class="p">.</span><span class="n">IsGenericType</span> <span class="p">&&</span>
<span class="n">x</span><span class="p">.</span><span class="nf">GetGenericTypeDefinition</span><span class="p">()</span> <span class="p">==</span> <span class="k">typeof</span><span class="p">(</span><span class="n">INotificationHandler</span><span class="p"><>))</span>
<span class="p">.</span><span class="nf">ToList</span><span class="p">();</span>
<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">handlerInterfaces</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">notificationType</span> <span class="p">=</span> <span class="n">item</span><span class="p">.</span><span class="n">GenericTypeArguments</span><span class="p">.</span><span class="nf">First</span><span class="p">();</span>
<span class="k">if</span><span class="p">(!</span><span class="n">_handlers</span><span class="p">.</span><span class="nf">TryGetValue</span><span class="p">(</span><span class="n">notificationType</span><span class="p">,</span><span class="k">out</span> <span class="kt">var</span> <span class="n">handlers</span><span class="p">)){</span>
<span class="n">handlers</span> <span class="p">=</span> <span class="k">new</span> <span class="n">List</span><span class="p"><(</span><span class="kt">object</span><span class="p">,</span> <span class="n">Func</span><span class="p"><</span><span class="n">SerializedNotification</span><span class="p">,</span> <span class="n">Task</span><span class="p">>)>();</span>
<span class="n">_handlers</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="n">notificationType</span><span class="p">,</span><span class="n">handlers</span><span class="p">);</span>
<span class="p">}</span>
<span class="n">handlers</span><span class="p">.</span><span class="nf">Add</span><span class="p">((</span><span class="n">handler</span><span class="p">,</span> <span class="k">async</span> <span class="n">s</span> <span class="p">=></span> <span class="k">await</span> <span class="n">handler</span><span class="p">.</span><span class="nf">Handle</span><span class="p">((</span><span class="n">T</span><span class="p">)</span><span class="n">s</span><span class="p">,</span> <span class="k">default</span><span class="p">(</span><span class="n">CancellationToken</span><span class="p">))));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">void</span> <span class="n">Unregister</span><span class="p"><</span><span class="n">T</span><span class="p">>(</span><span class="n">INotificationHandler</span><span class="p"><</span><span class="n">T</span><span class="p">></span> <span class="n">handler</span><span class="p">)</span> <span class="k">where</span> <span class="n">T</span> <span class="p">:</span> <span class="n">SerializedNotification</span>
<span class="p">{</span>
<span class="k">lock</span> <span class="p">(</span><span class="n">_handlers</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">_handlers</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">item</span><span class="p">.</span><span class="n">Value</span><span class="p">.</span><span class="nf">RemoveAll</span><span class="p">(</span><span class="n">h</span> <span class="p">=></span> <span class="n">h</span><span class="p">.</span><span class="n">Item1</span><span class="p">.</span><span class="nf">Equals</span><span class="p">(</span><span class="n">handler</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">static</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Publish</span><span class="p">(</span><span class="n">SerializedNotification</span> <span class="n">notification</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">try</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">notificationType</span> <span class="p">=</span> <span class="n">notification</span><span class="p">.</span><span class="nf">GetType</span><span class="p">();</span>
<span class="k">if</span><span class="p">(</span><span class="n">_handlers</span><span class="p">.</span><span class="nf">TryGetValue</span><span class="p">(</span><span class="n">notificationType</span><span class="p">,</span> <span class="k">out</span> <span class="kt">var</span> <span class="n">filtered</span><span class="p">)){</span>
<span class="k">foreach</span> <span class="p">(</span><span class="kt">var</span> <span class="n">item</span> <span class="k">in</span> <span class="n">filtered</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">item</span><span class="p">.</span><span class="nf">Item2</span><span class="p">(</span><span class="n">notification</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">catch</span> <span class="p">(</span><span class="n">System</span><span class="p">.</span><span class="n">Exception</span> <span class="n">e</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Console</span><span class="p">.</span><span class="n">Error</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="n">e</span> <span class="p">+</span> <span class="s">" "</span> <span class="p">+</span> <span class="n">e</span><span class="p">.</span><span class="n">StackTrace</span><span class="p">);</span>
<span class="k">throw</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>What it does is that it keeps in memory a list of all the implementation for a specific notification type (it took me a little while to figure out the Action thing)</li>
<li>The odd try catch is here mainly because SignalR is really shy and doesn’t say anything if there is an error</li>
</ul>
<p>Now to register to this my component needs to do this (in Index.razor for example) :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">@page</span> <span class="s">"/"</span>
<span class="n">@using</span> <span class="n">RemiBou</span><span class="p">.</span><span class="n">BlogPost</span><span class="p">.</span><span class="n">SignalR</span><span class="p">.</span><span class="n">Shared</span>
<span class="n">@using</span> <span class="n">MediatR</span>
<span class="n">@implements</span> <span class="n">INotificationHandler</span><span class="p"><</span><span class="n">CounterIncremented</span><span class="p">></span>
<span class="n">@implements</span> <span class="n">IDisposable</span>
<span class="p"><</span><span class="n">h1</span><span class="p">></span><span class="n">Hello</span><span class="p">,</span> <span class="n">world</span><span class="p">!</</span><span class="n">h1</span><span class="p">></span>
<span class="n">Welcome</span> <span class="n">to</span> <span class="n">your</span> <span class="k">new</span> <span class="n">app</span><span class="p">.</span>
<span class="n">Current</span> <span class="n">count</span> <span class="p">:</span> <span class="n">@count</span>
<span class="n">@code</span> <span class="p">{</span>
<span class="k">private</span> <span class="kt">int</span> <span class="n">count</span><span class="p">;</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">void</span> <span class="nf">OnInitialized</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">DynamicNotificationHandlers</span><span class="p">.</span><span class="nf">Register</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Handle</span><span class="p">(</span><span class="n">CounterIncremented</span> <span class="n">notification</span><span class="p">,</span> <span class="n">System</span><span class="p">.</span><span class="n">Threading</span><span class="p">.</span><span class="n">CancellationToken</span> <span class="n">cancellationToken</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">count</span> <span class="p">=</span> <span class="n">notification</span><span class="p">.</span><span class="n">Counter</span><span class="p">;</span>
<span class="nf">StateHasChanged</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">Dispose</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">DynamicNotificationHandlers</span><span class="p">.</span><span class="nf">Unregister</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>It listens to the notification for updating the UI</li>
<li>When the component is destroyed it stops listenning : DO NOT FORGET IT or your component will live for ever ever ever</li>
<li>This could be implemented as a BaseComponent</li>
</ul>
<p>Now the final wiring of SignalR to all this, in your client’s Program.cs :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">app</span> <span class="p">=</span> <span class="n">builder</span><span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">navigationManager</span> <span class="p">=</span> <span class="n">app</span><span class="p">.</span><span class="n">Services</span><span class="p">.</span><span class="n">GetRequiredService</span><span class="p"><</span><span class="n">NavigationManager</span><span class="p">>();</span>
<span class="kt">var</span> <span class="n">hubConnection</span> <span class="p">=</span> <span class="k">new</span> <span class="nf">HubConnectionBuilder</span><span class="p">()</span>
<span class="p">.</span><span class="nf">WithUrl</span><span class="p">(</span><span class="n">navigationManager</span><span class="p">.</span><span class="nf">ToAbsoluteUri</span><span class="p">(</span><span class="s">"/notifications"</span><span class="p">))</span>
<span class="p">.</span><span class="nf">AddJsonProtocol</span><span class="p">(</span><span class="n">o</span> <span class="p">=></span> <span class="n">o</span><span class="p">.</span><span class="n">PayloadSerializerOptions</span><span class="p">.</span><span class="n">Converters</span><span class="p">.</span><span class="nf">Add</span><span class="p">(</span><span class="k">new</span> <span class="nf">NotificationJsonConverter</span><span class="p">()))</span>
<span class="p">.</span><span class="nf">Build</span><span class="p">();</span>
<span class="n">hubConnection</span><span class="p">.</span><span class="n">On</span><span class="p"><</span><span class="n">SerializedNotification</span><span class="p">>(</span><span class="s">"Notification"</span><span class="p">,</span> <span class="k">async</span> <span class="p">(</span><span class="n">notificationJson</span><span class="p">)</span> <span class="p">=></span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">DynamicNotificationHandlers</span><span class="p">.</span><span class="nf">Publish</span><span class="p">(</span><span class="n">notificationJson</span><span class="p">);</span>
<span class="p">});</span>
<span class="k">await</span> <span class="n">hubConnection</span><span class="p">.</span><span class="nf">StartAsync</span><span class="p">();</span>
<span class="k">await</span> <span class="n">app</span><span class="p">.</span><span class="nf">RunAsync</span><span class="p">();</span>
</code></pre></div></div>
<ul>
<li>From the template you need to split the Build and RunAsync call so you get the service collection for your app.</li>
</ul>
<p>Et voila ! Now when you launch the bash script, the UI is updated automatically. Of course if you want to display the initial state you need to create an API that provides it.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Once again we saw the biggest selling point of Blazor : I can use the same toolbelt for the frontend and the backend (SignalR, MediatR, Json converters…) and it feels damn good :) You can now build a realtime app without a single line of javascript.</p>
<p>All the source code for this blog post is available here : [(https://github.com/RemiBou/remibou.github.io/tree/master/projects/RemiBou.BlogPost.SignalR]((https://github.com/RemiBou/remibou.github.io/tree/master/projects/RemiBou.BlogPost.SignalR)</p>How to have realtime update in your app with Blazor WASM, SignalR and MediatRHow To Keep Js Object Reference In Blazor2020-03-21T00:00:00+00:002020-03-21T00:00:00+00:00https://remibou.github.io/How-to-keep-js-object-reference-in-Blazor<h1 id="how-to-keep-javascript-object-reference-in-blazor-on-net-side-">How to keep Javascript object reference in Blazor on .NET side ?</h1>
<p>You can’t do everything in Blazor with .NET alone. If you want to use a JS lib or get information from the browser, you need to use <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/call-javascript-from-dotnet?view=aspnetcore-3.1">JSInterop</a>. When you need to communicate between 2 system or subsystem, you often need to “keep” a reference of something from the other side. For example when you call a “create payment” API, there is a lot of chance that it’ll send you a “paymentId” so you can call other API with this ID and do action like “refund” or “cancel”. For in-memory application it’s a bit cumbersome to do it with ID by hand, because :</p>
<ul>
<li>you have to maintain an in-memory map of the object you share with the other side</li>
<li>those object will never be release from memory by GC</li>
<li>you have to do it for every kind of interaction</li>
</ul>
<h2 id="how-does-the-framework-shares-net-instance-with-js-">How does the framework shares .NET instance with js ?</h2>
<p>It’s better to handle this kind of thing at the framework/infrastructure level so the developer experience is better. In Blazor there is already one mechanism like that : you can send to JS the reference of a .NET object, so you can call any method anotated with [JSInvokable] on this object.<a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/call-dotnet-from-javascript?view=aspnetcore-3.1"> From the official documentation</a>, you do it like this :</p>
<p>Razor file :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">@inject</span> <span class="n">IJSRuntime</span> <span class="n">_jsRuntime</span>
<span class="n">@implements</span> <span class="n">IDisposable</span>
<span class="p"><</span><span class="n">button</span> <span class="n">type</span><span class="p">=</span><span class="s">"button"</span> <span class="k">class</span><span class="err">="</span><span class="nc">btn</span> <span class="n">btn</span><span class="p">-</span><span class="n">primary</span><span class="s">" @onclick="</span><span class="n">TriggerNetInstanceMethod</span><span class="s">">
</span> <span class="n">Trigger</span> <span class="p">.</span><span class="n">NET</span> <span class="n">instance</span> <span class="n">method</span> <span class="n">HelloHelper</span><span class="p">.</span><span class="n">SayHello</span>
<span class="p"></</span><span class="n">button</span><span class="p">></span>
<span class="n">@code</span> <span class="p">{</span>
<span class="k">private</span> <span class="n">DotNetObjectReference</span><span class="p"><</span><span class="n">HelloHelper</span><span class="p">></span> <span class="n">_objRef</span><span class="p">;</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">TriggerNetInstanceMethod</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">_objRef</span> <span class="p">=</span> <span class="n">DotNetObjectReference</span><span class="p">.</span><span class="nf">Create</span><span class="p">(</span><span class="k">new</span> <span class="nf">HelloHelper</span><span class="p">(</span><span class="s">"Rémi"</span><span class="p">));</span>
<span class="kt">var</span> <span class="n">test</span> <span class="p">=</span> <span class="k">await</span> <span class="n">_jsRuntime</span><span class="p">.</span><span class="n">InvokeAsync</span><span class="p"><</span><span class="kt">string</span><span class="p">>(</span><span class="s">"exampleJsFunctions.sayHello"</span><span class="p">,</span> <span class="n">_objRef</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">void</span> <span class="nf">Dispose</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">_objRef</span><span class="p">?.</span><span class="nf">Dispose</span><span class="p">();</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">class</span> <span class="nc">HelloHelper</span>
<span class="p">{</span>
<span class="k">public</span> <span class="nf">HelloHelper</span><span class="p">(</span><span class="kt">string</span> <span class="n">name</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Name</span> <span class="p">=</span> <span class="n">name</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Name</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">[</span><span class="n">JSInvokable</span><span class="p">]</span>
<span class="k">public</span> <span class="kt">string</span> <span class="nf">SayHello</span><span class="p">()</span> <span class="p">=></span> <span class="s">$"Hello, </span><span class="p">{</span><span class="n">Name</span><span class="p">}</span><span class="s">!"</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>JS file</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nb">window</span><span class="p">.</span><span class="nx">exampleJsFunctions</span> <span class="o">=</span> <span class="p">{</span>
<span class="na">sayHello</span><span class="p">:</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">dotnetHelper</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">dotnetHelper</span><span class="p">.</span><span class="nx">invokeMethodAsync</span><span class="p">(</span><span class="dl">'</span><span class="s1">SayHello</span><span class="dl">'</span><span class="p">)</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">r</span> <span class="o">=></span> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">r</span><span class="p">));</span>
<span class="p">}</span>
<span class="p">};</span>
</code></pre></div></div>
<p>With this code, the “test” variable will be “Hello, Rémi”. How does it work ?</p>
<ul>
<li><a href="https://github.com/dotnet/aspnetcore/blob/5a0526dfd991419d5bce0d8ea525b50df2e37b04/src/JSInterop/Microsoft.JSInterop/src/DotNetObjectReference.cs">DotNetObjectReference</a>.Create instantiate a DotNetObjectReference<HelloHelper> with the given HelloHelper value.</li>
<li>The <a href="https://github.com/dotnet/aspnetcore/blob/5a0526dfd991419d5bce0d8ea525b50df2e37b04/src/JSInterop/Microsoft.JSInterop/src/JSRuntime.cs">JSRuntime</a> (method “TrackObjectReference”) detects that one parameter is a DotNetObjectReference and store it on an internal dictionary if it’s not already there, then increments an ID and send this id to javascript wrapped in a json object like this</li>
</ul>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="p">{</span>
<span class="dl">"</span><span class="s2">__dotNetObject</span><span class="dl">"</span><span class="p">:</span><span class="dl">"</span><span class="s2">1</span><span class="dl">"</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Where “1” is the object id on the internal dictionary.</p>
<ul>
<li>We saw on the <a href="https://remibou.github.io/">last blog post</a> how JSInterop uses json reviver for changing value received from .NET runtime. Here it uses the same mechanism for changing the json value to an instance of a DotnetObject like that</li>
</ul>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">dotNetObjectRefKey</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">__dotNetObject</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">attachReviver</span><span class="p">(</span><span class="kd">function</span> <span class="nx">reviveDotNetObject</span><span class="p">(</span><span class="nx">key</span><span class="p">:</span> <span class="nx">any</span><span class="p">,</span> <span class="nx">value</span><span class="p">:</span> <span class="nx">any</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">value</span> <span class="o">&&</span> <span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span> <span class="o">&&</span> <span class="nx">value</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">dotNetObjectRefKey</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="k">new</span> <span class="nx">DotNetObject</span><span class="p">(</span><span class="nx">value</span><span class="p">.</span><span class="nx">__dotNetObject</span><span class="p">);</span>
<span class="p">}</span>
<span class="c1">// Unrecognized - let another reviver handle it</span>
<span class="k">return</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div></div>
<ul>
<li>Then when the JS method receives an instance of a DotnetObject and calls “<a href="https://github.com/dotnet/aspnetcore/blob/5a0526dfd991419d5bce0d8ea525b50df2e37b04/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts#L58">invokeMethodAsync</a>” it calls a method called “invokeDotNetFromJS” on a JS interface called “DotNetCallDispatche” which is defined by the Blazor js library <a href="https://github.com/dotnet/aspnetcore/blob/2d0c49d0fca0aaf37672e0aec1c011cfe6a2d6f2/src/Components/Web.JS/src/Platform/Mono/MonoPlatform.ts#L257">here</a>.</li>
<li>This method accept 2 things as 2nd argument : an assembly name OR an object ID (value of field __dotNetObject) and the other arguments are the method name, call id (for managing async calls) and method parameters.</li>
<li>Then with the help of “<a href="https://github.com/mono/mono/blob/150ffb7af3ff53db44128fbe4513eda36d77529f/sdks/wasm/src/binding_support.js">bind_static_method</a>” from the mono WASM runtime which browse the loaded assemblies, it finds a reference to the C# static method “MonoWebAssemblyJSRuntime.BeginInvokeDotNet” and calls it (the method call itself <a href="https://github.com/mono/mono/blob/150ffb7af3ff53db44128fbe4513eda36d77529f/sdks/wasm/src/binding_support.js#L613">is really complicated</a>, it consist of playing with memory from the .NET runtime inside WebAssembly) with the assembly name or the object id, the method name and the method parameters.</li>
<li>This method then checks if the parameter is a digit, if it’s a digit then it’s a ref to a js object (if it’s not then it’s an assembly name). Why do they do a hack like that you will ask yourself ? Well there is a limit in interop between monowasm and blazor to 4 parameter, so they chose to send 2 distinct information into 1 slot. This limit does not apply to your code as every js interop call go through this wrapper.</li>
<li>Then with reflection the C# class called <a href="https://github.com/dotnet/aspnetcore/blob/5a0526dfd991419d5bce0d8ea525b50df2e37b04/src/JSInterop/Microsoft.JSInterop/src/Infrastructure/DotNetDispatcher.cs">DotNetDispatcher</a> will call the good method with the parameters.</li>
</ul>
<p>The only problem from a developer experience is that you have to think of your object disposal a bit just like if the GC doesn’t exist because a static reference to your instance is kept around. For disposing a DotNetObjectReference you need to call Dispose on it.</p>
<p>I don’t know why I went this far on the JSInterop explanation for this blog post, but I hope it’ll help someone understand a bit more how it works. Where are we now ? From this work I can identify how I have to do send js object reference to .NET :</p>
<ul>
<li>Store a method result to a map</li>
<li>Create a C# class for keeping the ID around</li>
<li>When this serialized C# class is send to js interop change it to the corresponding JS object</li>
<li>Provide a way for clearing the reference on the JS side</li>
</ul>
<h2 id="store-a-method-result-to-a-map">Store a method result to a map</h2>
<p>The first thing to do is to build the same thing but on js side. I first thought about using a <a href="https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Objets_globaux/WeakMap">WeakMap</a> but I don’t really understand how it can be useful as the key is the object on which we want to keep a weak reference. So, I use a simple javascript object. Here is my method for storing the object :</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">var</span> <span class="nx">jsObjectRefs</span> <span class="o">=</span> <span class="p">{};</span>
<span class="kd">var</span> <span class="nx">jsObjectRefId</span> <span class="o">=</span> <span class="mi">0</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">jsRefKey</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">__jsObjectRefId</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">function</span> <span class="nx">storeObjectRef</span><span class="p">(</span><span class="nx">obj</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">id</span> <span class="o">=</span> <span class="nx">jsObjectRefId</span><span class="o">++</span><span class="p">;</span>
<span class="nx">jsObjectRefs</span><span class="p">[</span><span class="nx">id</span><span class="p">]</span> <span class="o">=</span> <span class="nx">obj</span><span class="p">;</span>
<span class="kd">var</span> <span class="nx">jsRef</span> <span class="o">=</span> <span class="p">{};</span>
<span class="nx">jsRef</span><span class="p">[</span><span class="nx">jsRefKey</span><span class="p">]</span> <span class="o">=</span> <span class="nx">id</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">jsRef</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here is my sample js method calling it</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">function</span> <span class="nx">openWindow</span><span class="p">()</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">storeObjectRef</span><span class="p">(</span><span class="nb">window</span><span class="p">.</span><span class="nx">open</span><span class="p">(</span><span class="dl">"</span><span class="s2">/</span><span class="dl">"</span><span class="p">,</span> <span class="dl">"</span><span class="s2">_blank</span><span class="dl">"</span><span class="p">));</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And the JSInterop call</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">public</span> <span class="k">class</span> <span class="nc">JsRuntimeObjectRef</span>
<span class="p">{</span>
<span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"__jsObjectRefId"</span><span class="p">)]</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">JsObjectRefId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="p">}</span>
<span class="k">private</span> <span class="n">JsRuntimeObjectRef</span> <span class="n">_windowRef</span><span class="p">;</span>
<span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">OpenWindow</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">_windowRef</span> <span class="p">=</span> <span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="n">InvokeAsync</span><span class="p"><</span><span class="n">JsRuntimeObjectRef</span><span class="p">>(</span><span class="s">"openWindow"</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>I simplified the class : in Blazor the property is internal and they use a custom JsonConverter for serializing it while hidding it to users.</p>
<h2 id="using-the-reference-in-a-method-call">Using the reference in a method call</h2>
<p>Now I need to close this opened window, here is the JS method</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">closeWindow</span><span class="p">(</span><span class="nb">window</span><span class="p">)</span> <span class="p">{</span>
<span class="nb">window</span><span class="p">.</span><span class="nx">close</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And here is the C# interop call</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">CloseWindow</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">InvokeVoidAsync</span><span class="p">(</span><span class="s">"closeWindow"</span><span class="p">,</span> <span class="n">_windowRef</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You might be wondering : how does a JsRuntimeObjectRef becomes a window object on JS side ? With a reviver ! Here is its definition :</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">DotNet</span><span class="p">.</span><span class="nx">attachReviver</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">value</span> <span class="o">&&</span>
<span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span> <span class="o">&&</span>
<span class="nx">value</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">jsRefKey</span><span class="p">)</span> <span class="o">&&</span>
<span class="k">typeof</span> <span class="nx">value</span><span class="p">[</span><span class="nx">jsRefKey</span><span class="p">]</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">number</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">id</span> <span class="o">=</span> <span class="nx">value</span><span class="p">[</span><span class="nx">jsRefKey</span><span class="p">];</span>
<span class="k">if</span> <span class="p">(</span><span class="o">!</span><span class="p">(</span><span class="nx">id</span> <span class="k">in</span> <span class="nx">jsObjectRefs</span><span class="p">))</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="dl">"</span><span class="s2">This JS object reference does not exists : </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">id</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">const</span> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">jsObjectRefs</span><span class="p">[</span><span class="nx">id</span><span class="p">];</span>
<span class="k">return</span> <span class="nx">instance</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>
<p>This reviver will be called for every serialized object send to JS via JSInterop (even deep in the object graph, so you can send arrays or complex objects with JsRuntimeObjectRef properties).</p>
<p>You can find all the working code <a href="https://github.com/RemiBou/remibou.github.io/tree/master/projects/RemiBou.BlogPosts.JsReference">here</a>.</p>
<h2 id="cleaning-the-kept-reference-from-js-runtime-memory">Cleaning the kept reference from JS runtime memory.</h2>
<p>If we leave it like this, jsObjectRefs will keep a reference to js object forever which is bad and can impact your user experience (UX yeah). For removing the object reference in jsObjectRefs we’ll do a bit like with DotNetObjectReference and implement IAsyncDisposable in JsRuntimeObjectRef like this</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="k">public</span> <span class="k">class</span> <span class="nc">JsRuntimeObjectRef</span> <span class="p">:</span> <span class="n">IAsyncDisposable</span>
<span class="p">{</span>
<span class="k">internal</span> <span class="n">IJSRuntime</span> <span class="n">JSRuntime</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="nf">JsRuntimeObjectRef</span><span class="p">()</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"__jsObjectRefId"</span><span class="p">)]</span>
<span class="k">public</span> <span class="kt">int</span> <span class="n">JsObjectRefId</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">ValueTask</span> <span class="nf">DisposeAsync</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">JSRuntime</span><span class="p">.</span><span class="nf">InvokeVoidAsync</span><span class="p">(</span><span class="s">"browserInterop.removeObjectRef"</span><span class="p">,</span> <span class="n">JsObjectRefId</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">private</span> <span class="n">JsRuntimeObjectRef</span> <span class="n">_windowRef</span><span class="p">;</span>
<span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">OpenWindow</span><span class="p">()</span>
<span class="p">{</span>
<span class="n">_windowRef</span> <span class="p">=</span> <span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="n">InvokeAsync</span><span class="p"><</span><span class="n">JsRuntimeObjectRef</span><span class="p">>(</span><span class="s">"openWindow"</span><span class="p">);</span>
<span class="n">_windowRef</span><span class="p">.</span><span class="n">JSRuntime</span> <span class="p">=</span> <span class="n">jsRuntime</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">CloseWindow</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">InvokeVoidAsync</span><span class="p">(</span><span class="s">"closeWindow"</span><span class="p">,</span> <span class="n">_windowRef</span><span class="p">);</span>
<span class="k">await</span> <span class="n">_windowRef</span><span class="p">.</span><span class="nf">DisposeAsync</span><span class="p">();</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Because you need to set JSRuntime after every new JsRuntimeObjectRef creation, it might be a better idea to wrap this into an extension method.</p>
<p>Here is the JS method</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">cleanObjectRef</span><span class="p">(</span><span class="nx">id</span><span class="p">)</span> <span class="p">{</span>
<span class="k">delete</span> <span class="nx">jsObjectRefs</span><span class="p">[</span><span class="nx">jsObjectRefId</span><span class="p">];</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Now, when we close the opened window, the reference to said window will be removed and the browser will be able to GC it (if it feels like it).</p>
<h2 id="browserinterop">BrowserInterop</h2>
<p>On my last blog post I talked about my library <a href="https://www.nuget.org/packages/BrowserInterop">BrowserInterop</a> which is a library for making the developer life easier when he/she needs to use JSInterop. This library uses a lot of the things I talked about in this blog post because I need to keep reference of window object. For making my life easier I created a bunch of utility methods that you can use :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">IJSRuntime</span> <span class="n">jsRuntime</span><span class="p">;</span>
<span class="c1">// this will get a reference to the js window object that you can use later, it works like ElementReference ofr DotNetRef : you can add it to any method parameter and it </span>
<span class="c1">// will be changed in the corresponding js object </span>
<span class="kt">var</span> <span class="n">windowObjectRef</span> <span class="p">=</span> <span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="n">GetInstancePropertyAsync</span><span class="p"><</span><span class="n">JsRuntimeObjectRef</span><span class="p">>(</span><span class="s">"window"</span><span class="p">);</span>
<span class="c1">// get the value of window.performance.timeOrigin</span>
<span class="kt">var</span> <span class="n">time</span> <span class="p">=</span> <span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="n">GetInstancePropertyAsync</span><span class="p"><</span><span class="kt">decimal</span><span class="p">>(</span><span class="n">windowObjectRef</span><span class="p">,</span> <span class="s">"performance.timeOrigin"</span><span class="p">);</span>
<span class="c1">// set the value of the property window.history.scrollRestoration</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">SetInstancePropertyAsync</span><span class="p">(</span><span class="n">windowObjectRef</span><span class="p">,</span> <span class="s">"history.scrollRestoration"</span><span class="p">,</span> <span class="s">"auto"</span><span class="p">);</span>
<span class="c1">//get a reference to window.parent</span>
<span class="kt">var</span> <span class="n">parentRef</span> <span class="p">=</span> <span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">GetInstancePropertyRefAsync</span><span class="p">(</span><span class="n">windowObjectRef</span><span class="p">,</span> <span class="s">"parent"</span><span class="p">);</span>
<span class="c1">// call the method window.console.clear with window.console as scope</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">InvokeInstanceMethodAsync</span><span class="p">(</span><span class="n">windowObjectRef</span><span class="p">,</span> <span class="s">"console.clear"</span><span class="p">);</span>
<span class="c1">// call the method window.history.key(1) with window.history as scope</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="n">InvokeInstanceMethodAsync</span><span class="p"><</span><span class="kt">string</span><span class="p">>(</span><span class="n">windowObjectRef</span><span class="p">,</span> <span class="s">"history.key"</span><span class="p">,</span><span class="m">1</span> <span class="p">);</span>
<span class="c1">//will listen for the event until DisposeAsync is called on the result</span>
<span class="kt">var</span> <span class="n">listener</span> <span class="p">=</span> <span class="k">await</span> <span class="n">jSRuntime</span><span class="p">.</span><span class="nf">AddEventListener</span><span class="p">(</span><span class="n">windowObjectRef</span><span class="p">,</span> <span class="s">"navigator.connection"</span><span class="p">,</span> <span class="s">"change"</span><span class="p">,</span> <span class="p">()</span> <span class="p">=></span> <span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"navigator connection change"</span><span class="p">));</span>
<span class="c1">//stop listening to the event, you can also use "await using()" notation</span>
<span class="k">await</span> <span class="n">listener</span><span class="p">.</span><span class="nf">DisposeAsync</span><span class="p">();</span>
<span class="c1">//will return true if window.navigator.registerProtocolHandler property exists</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">HasProperty</span><span class="p">(</span><span class="n">windowObjectRef</span><span class="p">,</span> <span class="s">"navigator.registerProtocolHandler"</span><span class="p">)</span>
</code></pre></div></div>
<p>There is many methods, I will create blog pst about it soon, but you still can use it and send me feedback/bug reports.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I rant a lot in my head about the lack of hooks in ASPNET Core (HttpClient …) but the reviver one, while undocumented, is really great here.</p>How to keep Javascript object reference in Blazor on .NET side ?How To Send Callback To Js Interop In Blazor2020-02-29T00:00:00+00:002020-02-29T00:00:00+00:00https://remibou.github.io/How-to-send-callback-to-JS-Interop-in-Blazor<h1 id="how-to-send-callback-method-to-jsinterop-in-blazor-">How to send callback method to JSInterop in Blazor ?</h1>
<p>Blazor client-side or server side can handle only CPU bound calculation. For every interaction with the browser you need to use JSInterop. Even on Blazor itself the team uses JSInterop for using browser API like XHR request or DOM update.</p>
<p>Currently in the JS Interop API, you can do only one thing : call a given method connected to the “window” object with 3 kind of parameter :</p>
<ul>
<li>C# object serialized via System.Text.Json and deserialized via JSON.parse</li>
<li>Html element (ElementReference in C# side)</li>
<li>Reference to C# object on which you can then call any method annotated with “JSInvokable”</li>
</ul>
<p>But what about passing a callback method ? this would be useful if you want to register to an event like window.navigator.connection.onchange. With Blazor as-is, you can do it but you would have to do some plumbing and you would have to do it for every different callback you want to use. In this article I will show you how to do it in a more reusable way.</p>
<h2 id="json-reviver">Json Reviver</h2>
<p>As I said previously Blazor JSInterop uses JSON.parse for creating an object from a JSON string send by the .net runtime. Even ElementReference or DotNetObjectReference are serialized in JSON and sended to this method, <a href="https://github.com/dotnet/aspnetcore/blob/master/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts">here is the code that does it around line 217</a> :</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="kd">function</span> <span class="nx">parseJsonWithRevivers</span><span class="p">(</span><span class="nx">json</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="kr">any</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">json</span> <span class="p">?</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">json</span><span class="p">,</span> <span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">initialValue</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="c1">// Invoke each reviver in order, passing the output from the previous reviver,</span>
<span class="c1">// so that each one gets a chance to transform the value</span>
<span class="k">return</span> <span class="nx">jsonRevivers</span><span class="p">.</span><span class="nx">reduce</span><span class="p">(</span>
<span class="p">(</span><span class="nx">latestValue</span><span class="p">,</span> <span class="nx">reviver</span><span class="p">)</span> <span class="o">=></span> <span class="nx">reviver</span><span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">latestValue</span><span class="p">),</span>
<span class="nx">initialValue</span>
<span class="p">);</span>
<span class="p">})</span> <span class="p">:</span> <span class="kc">null</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div></div>
<p><a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse">JSON.parse</a> accepts a reviver parameter wich will be called for every field and every object of the json string. For instance for this js code</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">const</span> <span class="nx">json</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">{"result":true, "inner": {"count":42}}</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">obj</span> <span class="o">=</span> <span class="nx">JSON</span><span class="p">.</span><span class="nx">parse</span><span class="p">(</span><span class="nx">json</span><span class="p">,(</span><span class="nx">k</span><span class="p">,</span><span class="nx">v</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="nx">k</span><span class="p">,</span><span class="nx">v</span><span class="p">);</span> <span class="k">return</span> <span class="nx">v</span><span class="p">;}</span> <span class="p">);</span>
</code></pre></div></div>
<p>The console will output this</p>
<blockquote>
<p>“result” true<br />“count” 42<br />“inner” Object { count: 42 }<br />”” Object { result: true, inner: Object { count: 42 } }</p>
</blockquote>
<p>The reviver is called from the most nested property up to the root of the object. With this, the ASPNET Core team produced this reviver for changing a serialized ElementReference to the actual DOM element (<a href="https://github.com/dotnet/aspnetcore/blob/master/src/Components/Web.JS/src/Rendering/ElementReferenceCapture.ts">file</a>) :</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">function</span> <span class="nx">applyCaptureIdToElement</span><span class="p">(</span><span class="nx">element</span><span class="p">:</span> <span class="nx">Element</span><span class="p">,</span> <span class="nx">referenceCaptureId</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">element</span><span class="p">.</span><span class="nx">setAttribute</span><span class="p">(</span><span class="nx">getCaptureIdAttributeName</span><span class="p">(</span><span class="nx">referenceCaptureId</span><span class="p">),</span> <span class="dl">''</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">getElementByCaptureId</span><span class="p">(</span><span class="nx">referenceCaptureId</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">selector</span> <span class="o">=</span> <span class="s2">`[</span><span class="p">${</span><span class="nx">getCaptureIdAttributeName</span><span class="p">(</span><span class="nx">referenceCaptureId</span><span class="p">)}</span><span class="s2">]`</span><span class="p">;</span>
<span class="k">return</span> <span class="nb">document</span><span class="p">.</span><span class="nx">querySelector</span><span class="p">(</span><span class="nx">selector</span><span class="p">);</span>
<span class="p">}</span>
<span class="kd">function</span> <span class="nx">getCaptureIdAttributeName</span><span class="p">(</span><span class="nx">referenceCaptureId</span><span class="p">:</span> <span class="kr">string</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="s2">`_bl_</span><span class="p">${</span><span class="nx">referenceCaptureId</span><span class="p">}</span><span class="s2">`</span><span class="p">;</span>
<span class="p">}</span>
<span class="c1">// Support receiving ElementRef instances as args in interop calls</span>
<span class="kd">const</span> <span class="nx">elementRefKey</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">__internalId</span><span class="dl">'</span><span class="p">;</span> <span class="c1">// Keep in sync with ElementRef.cs</span>
<span class="nx">DotNet</span><span class="p">.</span><span class="nx">attachReviver</span><span class="p">((</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">value</span> <span class="o">&&</span> <span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span> <span class="o">&&</span> <span class="nx">value</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">elementRefKey</span><span class="p">)</span> <span class="o">&&</span> <span class="k">typeof</span> <span class="nx">value</span><span class="p">[</span><span class="nx">elementRefKey</span><span class="p">]</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">string</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">getElementByCaptureId</span><span class="p">(</span><span class="nx">value</span><span class="p">[</span><span class="nx">elementRefKey</span><span class="p">]);</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>
<p>When you add @ref to an element in Blazor, it adds an id “_bl_NUMBER” to the HTML element. The number is stored inside the ElementReference struct, initialized from a static int incremented for each ref or GUID. This work is done in <a href="https://github.com/dotnet/aspnetcore/blob/master/src/Components/Components/src/ElementReference.cs">this file</a> :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="k">public</span> <span class="k">readonly</span> <span class="k">struct</span> <span class="nc">ElementReference</span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">static</span> <span class="kt">long</span> <span class="n">_nextIdForWebAssemblyOnly</span> <span class="p">=</span> <span class="m">1</span><span class="p">;</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">Id</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="p">}</span>
<span class="k">public</span> <span class="nf">ElementReference</span><span class="p">(</span><span class="kt">string</span> <span class="n">id</span><span class="p">)</span>
<span class="p">{</span>
<span class="n">Id</span> <span class="p">=</span> <span class="n">id</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">internal</span> <span class="k">static</span> <span class="n">ElementReference</span> <span class="nf">CreateWithUniqueId</span><span class="p">()</span>
<span class="p">=></span> <span class="k">new</span> <span class="nf">ElementReference</span><span class="p">(</span><span class="nf">CreateUniqueId</span><span class="p">());</span>
<span class="k">private</span> <span class="k">static</span> <span class="kt">string</span> <span class="nf">CreateUniqueId</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="n">PlatformInfo</span><span class="p">.</span><span class="n">IsWebAssembly</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">id</span> <span class="p">=</span> <span class="n">Interlocked</span><span class="p">.</span><span class="nf">Increment</span><span class="p">(</span><span class="k">ref</span> <span class="n">_nextIdForWebAssemblyOnly</span><span class="p">);</span>
<span class="k">return</span> <span class="n">id</span><span class="p">.</span><span class="nf">ToString</span><span class="p">(</span><span class="n">CultureInfo</span><span class="p">.</span><span class="n">InvariantCulture</span><span class="p">);</span>
<span class="p">}</span>
<span class="k">else</span>
<span class="p">{</span>
<span class="k">return</span> <span class="n">Guid</span><span class="p">.</span><span class="nf">NewGuid</span><span class="p">().</span><span class="nf">ToString</span><span class="p">(</span><span class="s">"D"</span><span class="p">,</span> <span class="n">CultureInfo</span><span class="p">.</span><span class="n">InvariantCulture</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>They also created a JsonConverter for this type for keeping the property Id with no setter.</p>
<h2 id="custom-reviver-and-func-wrapper">Custom Reviver and Func wrapper</h2>
<p>Now we understand how they did it for ElementReference we can try to do it for Func. We need 2 thing :</p>
<ul>
<li>A C# wrapper that would keep a reference to my Func and a JSInvokable method that would call it</li>
<li>A JS reviver that would detect that the object sended is a Func wrapper and call the C# method.</li>
</ul>
<p>Here is my C# wrapper :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">public</span> <span class="k">class</span> <span class="nc">CallBackInteropWrapper</span>
<span class="p">{</span>
<span class="p">[</span><span class="nf">JsonPropertyName</span><span class="p">(</span><span class="s">"__isCallBackWrapper"</span><span class="p">)]</span>
<span class="k">public</span> <span class="kt">string</span> <span class="n">IsCallBackWrapper</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span> <span class="p">=</span> <span class="s">""</span><span class="p">;</span>
<span class="k">private</span> <span class="nf">CallBackInteropWrapper</span><span class="p">()</span>
<span class="p">{</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">static</span> <span class="n">CallBackInteropWrapper</span> <span class="n">Create</span><span class="p"><</span><span class="n">T</span><span class="p">>(</span><span class="n">Func</span><span class="p"><</span><span class="n">T</span><span class="p">,</span> <span class="n">Task</span><span class="p">></span> <span class="n">callback</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">res</span> <span class="p">=</span> <span class="k">new</span> <span class="n">CallBackInteropWrapper</span>
<span class="p">{</span>
<span class="n">CallbackRef</span> <span class="p">=</span> <span class="n">DotNetObjectReference</span><span class="p">.</span><span class="nf">Create</span><span class="p">(</span><span class="k">new</span> <span class="n">JSInteropActionWrapper</span><span class="p"><</span><span class="n">T</span><span class="p">>(</span><span class="n">callback</span><span class="p">))</span>
<span class="p">};</span>
<span class="k">return</span> <span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="k">static</span> <span class="n">CallBackInteropWrapper</span> <span class="nf">Create</span><span class="p">(</span><span class="n">Func</span><span class="p"><</span><span class="n">Task</span><span class="p">></span> <span class="n">callback</span><span class="p">)</span>
<span class="p">{</span>
<span class="kt">var</span> <span class="n">res</span> <span class="p">=</span> <span class="k">new</span> <span class="n">CallBackInteropWrapper</span>
<span class="p">{</span>
<span class="n">CallbackRef</span> <span class="p">=</span> <span class="n">DotNetObjectReference</span><span class="p">.</span><span class="nf">Create</span><span class="p">(</span><span class="k">new</span> <span class="nf">JSInteropActionWrapper</span><span class="p">(</span><span class="n">callback</span><span class="p">))</span>
<span class="p">};</span>
<span class="k">return</span> <span class="n">res</span><span class="p">;</span>
<span class="p">}</span>
<span class="k">public</span> <span class="kt">object</span> <span class="n">CallbackRef</span> <span class="p">{</span> <span class="k">get</span><span class="p">;</span> <span class="k">set</span><span class="p">;</span> <span class="p">}</span>
<span class="k">private</span> <span class="k">class</span> <span class="nc">JSInteropActionWrapper</span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">readonly</span> <span class="n">Func</span><span class="p"><</span><span class="n">Task</span><span class="p">></span> <span class="n">toDo</span><span class="p">;</span>
<span class="k">internal</span> <span class="nf">JSInteropActionWrapper</span><span class="p">(</span><span class="n">Func</span><span class="p"><</span><span class="n">Task</span><span class="p">></span> <span class="n">toDo</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">toDo</span> <span class="p">=</span> <span class="n">toDo</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">[</span><span class="n">JSInvokable</span><span class="p">]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Invoke</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">toDo</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">();</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="k">private</span> <span class="k">class</span> <span class="nc">JSInteropActionWrapper</span><span class="p"><</span><span class="n">T</span><span class="p">></span>
<span class="p">{</span>
<span class="k">private</span> <span class="k">readonly</span> <span class="n">Func</span><span class="p"><</span><span class="n">T</span><span class="p">,</span> <span class="n">Task</span><span class="p">></span> <span class="n">toDo</span><span class="p">;</span>
<span class="k">internal</span> <span class="nf">JSInteropActionWrapper</span><span class="p">(</span><span class="n">Func</span><span class="p"><</span><span class="n">T</span><span class="p">,</span> <span class="n">Task</span><span class="p">></span> <span class="n">toDo</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">toDo</span> <span class="p">=</span> <span class="n">toDo</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">[</span><span class="n">JSInvokable</span><span class="p">]</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Invoke</span><span class="p">(</span><span class="n">T</span> <span class="n">arg1</span><span class="p">)</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">toDo</span><span class="p">.</span><span class="nf">Invoke</span><span class="p">(</span><span class="n">arg1</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>I need to do it in 2 wrapper class : one for holding the information “this is a func wrapper” and one for holding the JSInvokable method.</li>
<li>I created 2 variant : one where the Func accept an argument and the other where is doesn’t, if I need to I will have to create one for each kind of Func I want to handle</li>
<li>I use Func<Task> so the callback can be asynchronous. I could also create overload for non Async callback but it would be too much noise.</li>
<li>My fields have getter and setter which is not really good, but System.Text.Json doesn’t provide an easy way to make those private unless you create your own JsonConverter. The best way to fix this in an lib would be to mark the type as internal and expose it as an interface.</li>
</ul>
<p>Here is the reviver in js</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">DotNet</span><span class="p">.</span><span class="nx">attachReviver</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">key</span><span class="p">,</span> <span class="nx">value</span><span class="p">)</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">value</span> <span class="o">&&</span>
<span class="k">typeof</span> <span class="nx">value</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span> <span class="o">&&</span>
<span class="nx">value</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="dl">"</span><span class="s2">__isCallBackWrapper</span><span class="dl">"</span><span class="p">))</span> <span class="p">{</span>
<span class="kd">var</span> <span class="nx">netObjectRef</span> <span class="o">=</span> <span class="nx">value</span><span class="p">.</span><span class="nx">callbackRef</span><span class="p">;</span>
<span class="k">return</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">netObjectRef</span><span class="p">.</span><span class="nx">invokeMethodAsync</span><span class="p">(</span><span class="dl">'</span><span class="s1">Invoke</span><span class="dl">'</span><span class="p">,</span> <span class="p">...</span><span class="nx">arguments</span><span class="p">);</span>
<span class="p">};</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">value</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">});</span>
</code></pre></div></div>
<ul>
<li>DotNet.attachReviver is a method of the JSInterop js library</li>
<li>“…arguments” means that I will send all the callback parameters to the “Invoke” method call as consecutive argument instead of an array of parameter.</li>
</ul>
<p>For using this I declare this js function</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">testCallback</span><span class="p">(</span><span class="nx">callback</span><span class="p">){</span>
<span class="k">if</span><span class="p">(</span><span class="nx">confirm</span><span class="p">(</span><span class="dl">'</span><span class="s1">are you sure ?</span><span class="dl">'</span><span class="p">)){</span>
<span class="nx">callback</span><span class="p">(</span><span class="dl">"</span><span class="s2">test</span><span class="dl">"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>And call it like that in the .net side</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">private</span> <span class="kt">string</span> <span class="n">callBackResult</span>
<span class="k">protected</span> <span class="k">override</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">OnInitializedAsync</span><span class="p">()</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">InvokeVoidAsync</span><span class="p">(</span><span class="s">"testCallback"</span><span class="p">,</span> <span class="n">CallBackInteropWrapper</span><span class="p">.</span><span class="n">Create</span><span class="p"><</span><span class="kt">string</span><span class="p">>(</span><span class="k">async</span> <span class="n">s</span> <span class="p">=></span> <span class="p">{</span>
<span class="k">this</span><span class="p">.</span><span class="n">callBackResult</span> <span class="p">=</span> <span class="n">s</span><span class="p">;</span>
<span class="k">this</span><span class="p">.</span><span class="nf">StateHasChanged</span><span class="p">();</span>
<span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">Completed</span><span class="p">;</span>
<span class="p">}));</span>
<span class="p">}</span>
</code></pre></div></div>
<h2 id="browserinterop">BrowserInterop</h2>
<p>Blazor without JSInterop is a bit hard because you often need to use some browser API : open a new window, get the geolocalization, get battery level etc … so I though about creating a library called “<a href="https://www.nuget.org/packages/BrowserInterop">BrowserInterop</a>” that would wrap all the JS Interop call regarding the browser API. You can have a look at the <a href="https://github.com/RemiBou/BrowserInterop">GitHub repository</a> for getting an idea about what I mean.</p>
<p>During the development of this library I needed to implement window event handling (like “onclose” or “connection.onchange”), so I developped the technique described earlier and a few more tools for helping .net developer to avoid writing js as much as possible (I take a bullet for everyone if you prefer).</p>
<p>BrowserInterop provides the wrapper I described before, you can use it like this (after following the package “Getting started”) :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kt">var</span> <span class="n">window</span> <span class="p">=</span> <span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">Window</span><span class="p">();</span>
<span class="kt">var</span> <span class="n">eventListening</span> <span class="p">=</span> <span class="k">await</span> <span class="n">window</span><span class="p">.</span><span class="n">OnMessage</span><span class="p"><</span><span class="kt">string</span><span class="p">>(</span><span class="k">async</span> <span class="p">(</span><span class="n">payload</span><span class="p">)</span> <span class="p">=></span> <span class="p">{</span>
<span class="n">onMessageEventPayload</span> <span class="p">=</span> <span class="n">payload</span><span class="p">;</span>
<span class="nf">StateHasChanged</span><span class="p">();</span>
<span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="n">CompletedTask</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div></div>
<ul>
<li>Window() is a BrowserInterop method which is the entrypoint to all the other BrowserInterop methods, it also gives information about the window object</li>
<li>OnMessage is an event handler for the “message” event on the window object. It’s usefull for cross window communication (a blog post will come about it)</li>
<li>The OnMessage returns an IAsyncDisposible that once disposed will stop listening, so you can stop listening to event when your component is disposed just like with C# event.</li>
<li>Thanks to BrowserInterop, I can read the message. In “vanilla” JSInterop you would have an empty object because informations in the “message” event payload are not serialized when sent to JSON.stringify, more about this on an other blog post.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>Even though C# developer dream (never touch JS again) is becoming true with Blazor, you still need to do some plumbing for talking with the browser. Let’s hope that some more library will remove this need in the future or maybe one day we’ll be able to use Browser API directly with WebAssembly.</p>How to send callback method to JSInterop in Blazor ?Profiling In Blazor2020-02-12T00:00:00+00:002020-02-12T00:00:00+00:00https://remibou.github.io/Profiling-in-Blazor<h1 id="how-to-profile-a-production-blazor-client-side-app-">How to profile a production Blazor client-side app ?</h1>
<p>Performance optimization is one of the programming task that I prefer. It’s really rewarding and requires knowledge, practice, creativity … The first thing you need to learn when optimizing performance is that <a href="https://twitter.com/SitnikAdam/status/1223263855989665792/photo/1">you should always profile before optimizing</a>. And always, when possible, favor production profiling instead of developer or staging profiling. Why ? Well you optimize for your production environment and this environment has different CPU, disk, memory, OS … specification than the other environment. So If you want your work to be useful, you need to profile production environment. For server-side profiling, with a .net stack there are many tools :</p>
<ul>
<li>WinDBG</li>
<li>MiniProfiler</li>
<li>SQL Server Profiler</li>
<li>Jetbrain dotTrace</li>
<li>APM provider (like Azure AppInsight or Datadog)</li>
<li>dotnet trace</li>
<li>many more</li>
</ul>
<p>Each of them has its role and if your team embrace DevOps values (“you build it, you ship it”) or you are just close to the operations then you should at least know them and know how to use them on your <strong>PRODUCTION</strong> environment. Let me insist one more time on this : optimizing without profiling production environment is nearly worthless. You can have result, but you can’t be sure about it, it will be only guessing. Given the fact that, most tof the time, optimizing makes your code harder to read : you prefer to do it only where it matters.</p>
<p>But you can’t deploy every try to see if it is efficient right ? For me the process is the following :</p>
<ul>
<li>Profile the production for finding on which code path you should work</li>
<li>Profile on your workstation to see how long this code path takes on your environment</li>
<li>Optimize on your workstation and profile after each optimization until this number is lower</li>
<li>Deploy your change to production (if your tests passes)</li>
<li>Profile the production and check if your optimization is truly efficient.</li>
<li>If you had to make your code less readable and your optimization is not efficient in production, then rollback your change and go back to square 1.</li>
</ul>
<p>But with Blazor we are working on the client-side, most of the tools I described can’t be used client-side (AFAIK) because they can’t be executed on your browser. In this blog post I will explain how you can profile a production Blazor WASM application.</p>
<p><em>I think this can also be used by Blazor server-side but the tools for profiling on the server are already enough.</em></p>
<h2 id="browser-profiling-tools">Browser profiling tools</h2>
<p>For this blog post I will use Google Chrome. Google Chrome provides a tools for profiling client-side code. To do so start of DevTools then :</p>
<ul>
<li>Tab “Performance”</li>
<li>Click on the record button on the top left of the frame</li>
<li>Execute the code you want to profile</li>
<li>Click on the stop button</li>
</ul>
<p>Then you will have something like that</p>
<p><img src="/assets/img/ScreenShot-Chrome-Profile.png" alt="Chrome Profile result" title="Chrome Profile result" /></p>
<p>I won’t describe you how to read everything in this profiling result (you can use <a href="https://developers.google.com/web/tools/chrome-devtools/rendering-tools">this doc</a>) but we can quickly understand the problem we faces with a Blazor WASM app : the executed .net code is behing a “wasm-function…” step and you can’t link it to your code. But there is already something interesting, you can see how much time the rendering (DOM update) is taking by searching for the method “renderBatch”.</p>
<h2 id="adding-custom-timings-to-profiling-results">Adding custom timings to profiling results</h2>
<p>The good news is, there is some Javascript API for adding informations to the profiler result : console.time and console.timeEnd. You can call it like that</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">@inject</span> <span class="n">IJSRuntime</span> <span class="n">jsRuntime</span><span class="p">;</span>
<span class="n">@code</span> <span class="p">{</span>
<span class="c1">//...</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Method</span><span class="p">(){</span>
<span class="k">try</span><span class="p">{</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">InvokeVoidAsync</span><span class="p">(</span><span class="s">"console.time"</span><span class="p">,</span> <span class="s">"sub task"</span><span class="p">);</span>
<span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">Delay</span><span class="p">(</span><span class="m">100</span><span class="p">);</span><span class="c1">//do something </span>
<span class="p">}</span>
<span class="k">finally</span><span class="p">{</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">InvokeVoidAsync</span><span class="p">(</span><span class="s">"console.timeEnd"</span><span class="p">,</span> <span class="s">"sub task"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">//...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>With this code, if you have a profiling session recording then the profiling result will display a line called “Console” which will display “sub task” in the profiling session. So you will be able to see how long it took for your code to run, how much CPU / Network / memory it used, how it relates to other profiled method call… :</p>
<p><img src="/assets/img/ScreenShot-Chrome-Profile-Console.png" alt="Chrome Profile result" title="Chrome Profile result" /></p>
<p>We can see the “sub task” label is displayed on the profiling results. If I create multiple label then I can easily identify which code is taking a long time to run.</p>
<h2 id="overhead">Overhead</h2>
<p>When you are profiling, the host is executing more stuff. So there is always an overhead in your profiling, always. Does it matter ? Yes and No :</p>
<ul>
<li>Yes : because if you add profling code in your production code, you don’t want it to slow down the production.</li>
<li>No : you don’t care if a function takes 100ms or 110ms when profling, you just want this number to go down.</li>
</ul>
<p>If I look at the profiling session I did on the previous paragraph, I can see that my method took 109.32ms instead of the 100ms expected. Why is that ? There is multiple potential culprit of this</p>
<ul>
<li>JS interop</li>
<li>“await” overhead in mono-wasm</li>
<li>task.Delay is not precise</li>
</ul>
<p>If I increase the Task.Delay to 200ms then the overhead is constant (+9,9ms) which seems normal because we are not doing CPU profiling. The overhead is not really a problem because the point is to treduce this number, the number itself is not very relevant. It can be relevant in some case, let’s say you have a spec like this “you need to have this done under 100ms” then you will have to take into account the overhead during your optimization work.</p>
<p>I did an other test and commented out the Task.Delay then the step is only 1,5ms, so in the profiling above we can say that 1.5ms are because of the interop call and 7.5ms are because of Task.Delay (which I think involves many jsinterop roundtrip).</p>
<p>The problem with this overhead (even if it’s low, we don’t know on which computer our user will run the code) is that you can’t keep this code in production because it would make performance worst in production. So you should put a flag before the profiling code and enable / disable it with an easter egg or something like that :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">@inject</span> <span class="n">IJSRuntime</span> <span class="n">jsRuntime</span><span class="p">;</span>
<span class="n">@code</span> <span class="p">{</span>
<span class="k">private</span> <span class="kt">bool</span> <span class="n">IsEnabled</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="c1">//...</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Method</span><span class="p">(){</span>
<span class="k">try</span><span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">IsEnabled</span><span class="p">)</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">InvokeVoidAsync</span><span class="p">(</span><span class="s">"console.time"</span><span class="p">,</span> <span class="s">"sub task"</span><span class="p">);</span>
<span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">Delay</span><span class="p">(</span><span class="m">100</span><span class="p">);</span><span class="c1">//do something </span>
<span class="p">}</span>
<span class="k">finally</span><span class="p">{</span>
<span class="k">if</span><span class="p">(</span><span class="n">IsEnabled</span><span class="p">)</span>
<span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">InvokeVoidAsync</span><span class="p">(</span><span class="s">"console.timeEnd"</span><span class="p">,</span> <span class="s">"sub task"</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">//...</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>There is still the overhead of the larger downloaded code and “if” evaluation but this is not relevant</li>
<li>This code is highly repetitive and should be refactored</li>
</ul>
<h2 id="the-browserinterop-package">The BrowserInterop package</h2>
<p>At first I wanted to create a sub project in MiniProfiler but <a href="https://github.com/MiniProfiler/dotnet/issues/441">I got some problem with Blazor and MiniProfiler</a>. Then I found out about the browser API and I thougght about creating a package that would wrap all the browser APIs and make them easier to use by a C# developer :</p>
<ul>
<li>Use IDisposable like MiniProfiler</li>
<li>Use better typing when I can (enum, timespan, datetime …)</li>
<li>Create helper for handling case not handled by builtin library like event handling or JS variable reference.</li>
</ul>
<p>So I created the package <a href="https://www.nuget.org/packages/BrowserInterop">BrowserInterop</a> you can install it with this command</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>dotnet add package BrowserInterop
</code></pre></div></div>
<p>Then use it like that</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">@inject</span> <span class="n">IJSRuntime</span> <span class="n">jsRuntime</span><span class="p">;</span>
<span class="n">@using</span> <span class="n">BrowserInterop</span> <span class="p">;</span>
<span class="n">@code</span> <span class="p">{</span>
<span class="k">private</span> <span class="kt">bool</span> <span class="n">IsEnabled</span> <span class="p">=</span> <span class="k">false</span><span class="p">;</span>
<span class="c1">//...</span>
<span class="k">public</span> <span class="k">async</span> <span class="n">Task</span> <span class="nf">Method</span><span class="p">(){</span>
<span class="kt">var</span> <span class="n">window</span> <span class="p">=</span> <span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">Window</span><span class="p">();</span>
<span class="k">await</span> <span class="k">using</span><span class="p">(</span><span class="k">await</span> <span class="n">window</span><span class="p">.</span><span class="n">Console</span><span class="p">.</span><span class="nf">Time</span><span class="p">(</span><span class="s">"sub task"</span><span class="p">))</span>
<span class="p">{</span>
<span class="k">await</span> <span class="n">Task</span><span class="p">.</span><span class="nf">Delay</span><span class="p">(</span><span class="m">200</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="c1">//...</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Wrapping the code in a using statement makes it easier to read. And because I refactored the interop call, I was able to provide a way for disabling it easily like that</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">ConsoleInterop</span><span class="p">.</span><span class="n">IsEnabled</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
</code></pre></div></div>
<p>With this value set to false, all calls to any method inside window.Console will be ignored. With a well placed easter egg (hidden button or double click on the header) you can enable or disable this in production.</p>
<p>For now this package provides all the method in window.console and many method and fields in window.navigator and window. You can find out more on the project repository here <a href="https://github.com/RemiBou/BrowserInterop">https://github.com/RemiBou/BrowserInterop</a>. I will publish the package v1 once I’m done with the window API (alert, frames, performance …). Do not hesitate to try and and send me feedback on the repo, here, on twitter, by mail or anything.</p>
<h2 id="conclusion">Conclusion</h2>
<p>Being able to profile live app is a real advantage and the only way for getting valuable results. Do not forget to profile your app on different computer / Browser / OS as most of the time you have no impact on those. I hope my solution and my package will help you.</p>How to profile a production Blazor client-side app ?Test Your Jsinterop Code With Cypress2020-01-29T00:00:00+00:002020-01-29T00:00:00+00:00https://remibou.github.io/Test-your-JSInterop-code-with-cypress<h1 id="test-your-jsinterop-code-with-cypressio">Test your JSInterop code with cypress.io</h1>
<p><a href="https://www.cypress.io/">Cypress.io</a> is a game changer in the world of web E2E test. So far it was dominated by WebDriver based framework but it has the following advantages :</p>
<ul>
<li>It’s easy to setup</li>
<li>It’s easy to integrate into a CI pipeline</li>
<li>The API are fine (I still don’t like the assertion methods)</li>
<li>The debug information it provides are golden and makes your tests easy to fix</li>
<li>There is a lot of methods for making your tests less flaky (you do’nt have to add random wait every 2 lines)</li>
</ul>
<p>The only disadvantage being the maturity of the tool so there is some missing pieces like built-in file upload or spying of fetch request but the community is quite large and there is always a 3rd party script/lib for fixing what is missing.</p>
<h2 id="how-to-test-for-a-method-call">How to test for a method call</h2>
<p>In cypress there is multiple methods for spying or stubbing the navigator methods for instance :</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nx">context</span><span class="p">(</span><span class="dl">'</span><span class="s1">window.console</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">before</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">visit</span><span class="p">(</span><span class="dl">'</span><span class="s1">/</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">Check console methods called</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nb">window</span><span class="p">()</span>
<span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">w</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">spy</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">console</span><span class="p">,</span> <span class="dl">"</span><span class="s2">log</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#btn-console-do-test</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">()</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">).</span><span class="nx">be</span><span class="p">.</span><span class="nx">called</span><span class="p">.</span><span class="nx">calledTwice</span><span class="p">;</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">);</span>
</code></pre></div></div>
<p>This test clicks on a button and then expect the console.log to be called twice.</p>
<h2 id="the-problem-with-jsinterop">The problem with JSInterop</h2>
<p>This would work very well in a pure js application. If the console.log method calls are done with JSInterop like in a Blazor WASM app :</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">await</span> <span class="n">jsRuntime</span><span class="p">.</span><span class="nf">InvokeVoidAsync</span><span class="p">(</span><span class="s">"console.log"</span><span class="p">,</span><span class="s">"test"</span><span class="p">);</span>
<span class="c1">//or</span>
<span class="n">Console</span><span class="p">.</span><span class="nf">WriteLine</span><span class="p">(</span><span class="s">"test"</span><span class="p">);</span>
</code></pre></div></div>
<p>This would fail with this error :</p>
<div class="language-plaintext highlighter-rouge"><div class="highlight"><pre class="highlight"><code>blazor.webassembly.js:1 WASM: Unhandled exception rendering component:
blazor.webassembly.js:1 WASM: Microsoft.JSInterop.JSException: The value 'window.console.log' is not a function.
blazor.webassembly.js:1 WASM: Error: The value 'window.console.log' is not a function.
blazor.webassembly.js:1 WASM: at p (http://localhost:5000/_framework/blazor.webassembly.js:1:9063)
blazor.webassembly.js:1 WASM: at http://localhost:5000/_framework/blazor.webassembly.js:1:9605
blazor.webassembly.js:1 WASM: at new Promise (<anonymous>)
blazor.webassembly.js:1 WASM: at Object.beginInvokeJSFromDotNet (http://localhost:5000/_framework/blazor.webassembly.js:1:9579)
blazor.webassembly.js:1 WASM: at _mono_wasm_invoke_js_marshalled (http://localhost:5000/_framework/wasm/mono.js:1:165611)
blazor.webassembly.js:1 WASM: at wasm-function[6221]:0x11936a
blazor.webassembly.js:1 WASM: at wasm-function[1431]:0x402ee
blazor.webassembly.js:1 WASM: at wasm-function[636]:0x147cf
blazor.webassembly.js:1 WASM: at wasm-function[4996]:0xeb135
blazor.webassembly.js:1 WASM: at wasm-function[3247]:0xa0666
blazor.webassembly.js:1 WASM: at System.Threading.Tasks.ValueTask`1[TResult].get_Result () <0x20a9640 + 0x0002c> in <5745b1bd6f4246d7aee8c81307e6355a>:0
blazor.webassembly.js:1 WASM: at Microsoft.JSInterop.JSRuntimeExtensions.InvokeVoidAsync (Microsoft.JSInterop.IJSRuntime jsRuntime, System.String identifier, System.Object[] args) <0x2081800 + 0x000e4> in <3eedf0ca90ca4e72bf6870618ca98c7c>:0
</code></pre></div></div>
<p>This is due to this code in Microsoft.JSInterop you can find in <a href="https://github.com/dotnet/extensions/blob/master/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts">this file</a> :</p>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">findJSFunction</span><span class="p">(</span><span class="nx">identifier</span><span class="p">:</span> <span class="kr">string</span><span class="p">):</span> <span class="nb">Function</span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">cachedJSFunctions</span><span class="p">.</span><span class="nx">hasOwnProperty</span><span class="p">(</span><span class="nx">identifier</span><span class="p">))</span> <span class="p">{</span>
<span class="k">return</span> <span class="nx">cachedJSFunctions</span><span class="p">[</span><span class="nx">identifier</span><span class="p">];</span>
<span class="p">}</span>
<span class="kd">let</span> <span class="nx">result</span><span class="p">:</span> <span class="kr">any</span> <span class="o">=</span> <span class="nb">window</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">resultIdentifier</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">window</span><span class="dl">'</span><span class="p">;</span>
<span class="kd">let</span> <span class="nx">lastSegmentValue</span><span class="p">:</span> <span class="kr">any</span><span class="p">;</span>
<span class="nx">identifier</span><span class="p">.</span><span class="nx">split</span><span class="p">(</span><span class="dl">'</span><span class="s1">.</span><span class="dl">'</span><span class="p">).</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">segment</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">segment</span> <span class="k">in</span> <span class="nx">result</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">lastSegmentValue</span> <span class="o">=</span> <span class="nx">result</span><span class="p">;</span>
<span class="nx">result</span> <span class="o">=</span> <span class="nx">result</span><span class="p">[</span><span class="nx">segment</span><span class="p">];</span>
<span class="nx">resultIdentifier</span> <span class="o">+=</span> <span class="dl">'</span><span class="s1">.</span><span class="dl">'</span> <span class="o">+</span> <span class="nx">segment</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">`Could not find '</span><span class="p">${</span><span class="nx">segment</span><span class="p">}</span><span class="s2">' in '</span><span class="p">${</span><span class="nx">resultIdentifier</span><span class="p">}</span><span class="s2">'.`</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">});</span>
<span class="k">if</span> <span class="p">(</span><span class="nx">result</span> <span class="k">instanceof</span> <span class="nb">Function</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">lastSegmentValue</span><span class="p">);</span>
<span class="nx">cachedJSFunctions</span><span class="p">[</span><span class="nx">identifier</span><span class="p">]</span> <span class="o">=</span> <span class="nx">result</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">`The value '</span><span class="p">${</span><span class="nx">resultIdentifier</span><span class="p">}</span><span class="s2">' is not a function.`</span><span class="p">);</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div></div>
<p>You see the problem ? No ? Well it’s not obvious :</p>
<ul>
<li>In JS types are defined by window. eg : if you have an iframe then the type “Function” inside the iframe is not the same as the “Function” type in the parent.</li>
<li>Cypress uses iframes for running the tests (that’s why you are not limited like in WebDriver)</li>
<li>when you call cy.spy, it changes the definition of console.log, so its type becomes a “Function” in the context of the runner iframe, not the app.</li>
</ul>
<h2 id="how-do-i-fix-this-">How do I fix this ?</h2>
<p>Fortunately Javascript allows us to do very stupid things, like changing an object prototype on the fly ! After the fix, my Cypress code test looks this :</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nx">context</span><span class="p">(</span><span class="dl">'</span><span class="s1">window.console</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">before</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">visit</span><span class="p">(</span><span class="dl">'</span><span class="s1">/console</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">Check console methods called</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nb">window</span><span class="p">()</span>
<span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">w</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">spy</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">console</span><span class="p">,</span> <span class="dl">"</span><span class="s2">log</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">w</span><span class="p">.</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">.</span><span class="nx">__proto__</span> <span class="o">=</span> <span class="nx">w</span><span class="p">.</span><span class="nb">Function</span><span class="p">;</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#btn-console-do-test</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">()</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">).</span><span class="nx">be</span><span class="p">.</span><span class="nx">called</span><span class="p">.</span><span class="nx">calledThrice</span><span class="p">;</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">);</span>
</code></pre></div></div>
<p>Notice the line after the spy (<a href="https://docs.cypress.io/api/commands/spy.html#Syntax">which is synchronous</a>) which changes console.log prototype, this makes the “instanceof Function” condition pass and my test run successfully.</p>
<p>I created the following Cypress command to reduce code duplication</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code>
<span class="nx">Cypress</span><span class="p">.</span><span class="nx">Commands</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="dl">'</span><span class="s1">spyFix</span><span class="dl">'</span><span class="p">,</span> <span class="p">(</span><span class="nx">object</span><span class="p">,</span> <span class="nx">method</span><span class="p">,</span> <span class="nb">window</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">spy</span><span class="p">(</span><span class="nx">object</span><span class="p">,</span> <span class="nx">method</span><span class="p">);</span>
<span class="nx">object</span><span class="p">[</span><span class="nx">method</span><span class="p">].</span><span class="nx">__proto__</span> <span class="o">=</span> <span class="nb">window</span><span class="p">.</span><span class="nb">Function</span><span class="p">;</span>
<span class="p">});</span>
<span class="nx">context</span><span class="p">(</span><span class="dl">'</span><span class="s1">window.console</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">before</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">visit</span><span class="p">(</span><span class="dl">'</span><span class="s1">/console</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">Check console methods called</span><span class="dl">'</span><span class="p">,</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nb">window</span><span class="p">()</span>
<span class="p">.</span><span class="nx">then</span><span class="p">((</span><span class="nx">w</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">spyFix</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">console</span><span class="p">,</span> <span class="dl">"</span><span class="s2">log</span><span class="dl">"</span><span class="p">,</span> <span class="nx">w</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#btn-console-do-test</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">()</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">w</span><span class="p">.</span><span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">).</span><span class="nx">be</span><span class="p">.</span><span class="nx">called</span><span class="p">.</span><span class="nx">calledThrice</span><span class="p">;</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="p">}</span>
<span class="p">);</span>
</code></pre></div></div>
<h2 id="conclusion">Conclusion</h2>
<p>I’m happy I found an easy way to fix this, there is other ways with “new win.Function” or by overriding some method in window.DotNet but they require more line of code and are much more complicated.</p>Test your JSInterop code with cypress.ioShould I Peek Blazor2020-01-16T00:00:00+00:002020-01-16T00:00:00+00:00https://remibou.github.io/Should-I-peek-Blazor<h1 id="should-i-peek-blazor-">Should I peek Blazor ?</h1>
<p><em>ALL THIS BLOG POST CONTENT IS MY OPINION. IF YOU HAVE A DIFFERENT ONE PLEASE POST A COMMENT</em></p>
<p>On Twitter or Reddit I often see question about whether Blazor is a good choice. In this blog post I will try to ask questions that might help you making this decision.</p>
<h2 id="quick-presentation">Quick presentation</h2>
<h3 id="what-is-blazor-">What is Blazor ?</h3>
<p>You will get a good description of Blazor from the official website : <a href="https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor">https://dotnet.microsoft.com/apps/aspnet/web-apps/blazor</a>. It belongs to the family of the SPA framework like Angular, react or vue.</p>
<p>Blazor is a framework for building web UI using C#. It has 2 ways of working :</p>
<ul>
<li>Client-Side : This way of working looks a lot like Angular or React. Your netsandard assemblies are downloaded on the browser and executed by a .NET runtime build for WebAssembly. This runtime is called “monowasm” it is developped by the mono team (I think this is because they have the experience in working with environment with low ressources like a browser or a phone).</li>
</ul>
<p><img src="https://docs.microsoft.com/fr-fr/aspnet/core/blazor/index/_static/blazor-webassembly.png?view=aspnetcore-3.1" alt="Blazor client-side" title="Blazor client-side" /></p>
<ul>
<li>Server-Side : A SignalR connection between the browser and the server is opened, when something happens on one of the side (eg : a click on a button), the server sends the DOM changes that must be done to the client. I think this was created.</li>
</ul>
<p><img src="https://docs.microsoft.com/fr-fr/aspnet/core/blazor/index/_static/blazor-server.png?view=aspnetcore-3.1" alt="Blazor server-side" title="Blazor server-side" /></p>
<h2 id="should-i-use-it-">Should I use it ?</h2>
<p>It depends.</p>
<p>This is very broad question to which I will answer like a politician : with more questions :).</p>
<h3 id="should-you-use-an-spa-framework-in-the-first-time-">Should you use an SPA framework in the first time ?</h3>
<p>You never start a project by saying “I need a database”. You analyze your spec and read that you need to store some data, and you decide to use a relationnal or document database.
Here it’s the same thing. I make the distinction between a web application and a web site :</p>
<ul>
<li>A web application requires a lot of interaction with the user : video game (like <a href="https://www.ageofascent.com/">https://www.ageofascent.com/</a>), SaaS (like Office365), an intranet or a tool (like pgadmin), there is little to no need for SEO, first load time can be a bit long and most of the users are active.</li>
<li>A web site is a collection of web page where the user navigates between them with link. Most fo the workload is read only, there is a LOT of traffic, most of the users come and go, SEO needs is crucial, page must be displayed in less than 100ms …</li>
</ul>
<p>Most of the time a project is a combination of both : you’ll build a cms (web app) that will generate a blog (web site). Or there is a shopping cart (web app) part in a online retailer site (web site).
I would still suggest that either you split the 2 aspectss in 2 distinct projects using 2 differents technologies or you choose one and pick your toolchain with it.</p>
<p>2 examples :</p>
<ul>
<li>StackOverflow is a website even though there is some forms for submiting content, and searching things. So they adopted technologies for building web site : server side rendering with some jQuery.</li>
<li>Facebook is a web app even though there is a lot of readonly load and SEO needs are important. But this level of interaction would be very hard to obtain with jQuery.</li>
</ul>
<p>From the question above I can draw this conclusion : <strong>IF YOU ARE BUILDING A WEB SITE, CLOSE THIS WINDOW, YOU DON’T NEED BLAZOR</strong>, you don’t need Angular or react, just use server side rendering like ASPNET Core MVC or Symphony and you will be ok.</p>
<h4 id="isnt-it-silverlight-over-again-">Isn’t it Silverlight over again ?</h4>
<p>Silverlight or Flash were used for creating Web UI. But that’s all there is in common between Blazor and Silverlight or Flash :</p>
<ul>
<li>Blazor uses Razor template language, so it uses web standards for managing UI components : HTML, CSS, JS, WebAssembly, WebSocket …</li>
<li><a href="https://github.com/dotnet/aspnetcore/tree/master/src/Components">Blazor is open source</a>.</li>
<li>It doesn’t require any security settings on the client.</li>
<li>It doesn’t require any 3rd party install on the client.</li>
<li>It cannot do anything in the user computer that JS or WebAssembly cannot do.</li>
</ul>
<p>Results : this doesn’t change anything.</p>
<h4 id="isnt-it-webforms-over-again-with-the-infamous-viewstate-">Isn’t it WebForms over again with the infamous ViewState ?</h4>
<p>WebForms was used for building Web UI and was provided by .NET but there is a lot of difference with Blazor :</p>
<ul>
<li>Blazor does not create an abstraction on the produced HTML or JS or CSS.</li>
<li><a href="https://github.com/dotnet/aspnetcore/tree/master/src/Components">Blazor is open source</a>.</li>
<li>Blazor client-side is executed on the client.</li>
<li>There is no state exchange between client and server with Blazor server-side (like ViewState).</li>
</ul>
<p>Results : this doesn’t change anything.</p>
<h3 id="do-we-have-strong-seo-needs-">Do we have strong SEO needs ?</h3>
<p>Most of the search engine bot in the world don’t execute any client-side code. <a href="https://developers.google.com/search/docs/guides/javascript-seo-basics">Google Bot executes client-side code</a> because it uses a headless chromium, but they recommend using server side rendering because their robot will index your website faster.</p>
<p>Blazor server side provides server-side pre-rendering : on the first request, full HTML page is computed and send to the web client (browser or bot), so you shouldn’t have any SEO problems here.</p>
<p>But for Blazor client-side only the empty index.html is send to the client so the bot has to execute some code and I am not sure Google bot allows WebAssembly execution (I should test it).</p>
<p>Results : If you have strong SEO needs, Blazor client-side is not recommended.</p>
<h3 id="do-you-need-to-do-dom-manipulation-">Do you need to do DOM manipulation ?</h3>
<p>While it’s a bad practice to do it with a framework like this, some people will still do it. Here you can do it with JS interop but there is no guarantee that your change won’t be erased because Blazor keeps a state of the app in memory.</p>
<p>Results : this doesn’t change anything, I don’t think you should do DOM manipulation with a front-end framework.</p>
<h3 id="does-your-app-needs-to-be-available-offline-">Does your app needs to be available offline ?</h3>
<p>When you are using a web application on your phone, the internet conection is not always stable. In Google Doc this is not a problem and your change will be saved when the connection is restored. With Blazor client-side there is no problem as long as you manage the connection error on your code (and you can use <a href="https://github.com/App-vNext/Polly">Polly</a> for that), you will be fine.</p>
<p>But in Blazor server-side the GUI state is stored on the server so if your user looses the connection, the application will stop working and the user might fail to recover his session when the connection is back but <a href="https://docs.microsoft.com/en-us/aspnet/core/blazor/hosting-models?view=aspnetcore-3.1#blazor-server">depending on the duration of the outage</a> it might not be possible. Here is the result for the user when reonnection fails :</p>
<p><img src="/assets/img/ScreenShot-Blazor-Reconnect.png" alt="Blazor server-side reconnect fails" title="Blazor server-side reconnect fails" /></p>
<p>Results : this is the biggest downside of Blazor server-side. If it’s a problem, depending on your load, you could increase the timeout before a session is destroyed after a disconnect.</p>
<h3 id="does-bazor-misses-anything-from-other-spa-framework-">Does Bazor misses anything from other SPA framework ?</h3>
<p>So far Blazor provides all the features you need for building a complete application : templating, forms, network, authentication, authorization, libraries etc … like with any framework, stop wondering “Where is the feature Y that exists in framework X ?” but read the documentation and follow the framework principles or you will miss its added value : if you stop reading about Blazor because it doesn’t provide decorator like in Angular you will miss features like CascadingParameter that are not in Angular.</p>
<p>Even though Blazor exists since only a dozen months, you can use all the netstandard library available in <a href="nuget.org">nuget.org</a> for your frontend project and that is a huge selling point. When a new technology comes around, you have to wait a bit until there is nice community project emerging or until the included libs are stable. This is not the case here : most of the Base Class LIbrary is copied from the mono runtime and the open source community produces a lot of great library.</p>
<p>Results : Blazor is already a great/stable/complete framework with many tools and don’t try to use Blazor like Angular (vice-versa).</p>
<h3 id="do-we-need-to-change-our-licensing-for-using-blazor-">Do we need to change our licensing for using Blazor ?</h3>
<p>Blazor is part of the <a href="https://github.com/dotnet/aspnetcore">ASPNET Core repository</a> and is released under the Apache 2.0 license. In my understanding, from a user point of view there is no constraint.</p>
<p>Results : this doesn’t change anything.</p>
<h3 id="do-we-need-to-upgrade-our-server-spec-for-using-blazor-">Do we need to upgrade our server spec for using Blazor ?</h3>
<p>Blazor client-side binaries are heavy : <a href="https://channel9.msdn.com/Events/dotnetConf/Focus-on-Blazor/Blazor-Futures-WebAssembly-PWAs-Hybrid-Native">around 2Mo for the default app</a> and most of it is the runtime and the system libraries, so when your app will grow you won’t see this number growing much. Unless you add reference to heavy library (<a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/blazor/configure-linker?view=aspnetcore-3.1">and still there is the illinker</a> that will remove any unused part o the assemblies).</p>
<p>Blazor server-side can be scary from a scale point of view: all your user GUI state will be stored server side and you will keep an opened connection during their whole session. But the ASPNET Team is awesome. Seriously, they did some measurement and the result should be enough for 99.9999% of the app out there (don’t make architecture choice based on Twitter, Netflix or Pornhub scale). Here is a screenshot of a presentation Dan Roth (Program manager at MSFT) did in Jan ‘20 :</p>
<p><img src="/assets/img/ScreenShot-Blazor-Serer-Perf.png" alt="Blazor server-side performance" title="Blazor server-side performance" /></p>
<p>5000 concurrent connection is a LOT, if you have this level of load and every user stays 1 hour on your app, it’s 3,7 Million monthly users with a 1 CPU and 3.5 GB RAM server.</p>
<p>Results : server side performance are really great and shouldn’t need any investment</p>
<h3 id="does-the-user-will-have-a-good-experience-with-our-app-">Does the user will have a good experience with our app ?</h3>
<p>Client-side performance is a difficult subject as there is much more variable than with server-side. You have to take into account the client envionment : latency, bandwidth, CPU, memory, other app running … But there is some tools for getting an idea of the user exeperience.</p>
<p>I did some tests with <a href="https://developers.google.com/web/tools/lighthouse/">Lighthouse</a>. I decided to create a small example where I compare 3 app created from the cli, one Blazor client-side, one Blazor server-side and one with Angular 8. You can find the code <a href="https://github.com/RemiBou/Blog-angular-blazor-performance">here</a>.
Because the tests where done locally the Time To First Byte is not relevant. The build and run were done with production settings (Release configuration and –prod flag).</p>
<h4 id="results-with-cpu-and-bandwidth-throtling-on">Results with CPU and bandwidth throtling on</h4>
<table>
<thead>
<tr>
<th>Metric</th>
<th style="text-align: center">Blazor Client Side</th>
<th style="text-align: center">Blazor Server Side</th>
<th style="text-align: right">Angular</th>
</tr>
</thead>
<tbody>
<tr>
<td>Performance score</td>
<td style="text-align: center">65</td>
<td style="text-align: center">88</td>
<td style="text-align: right">98</td>
</tr>
<tr>
<td>First contentful paint</td>
<td style="text-align: center">1.5sec</td>
<td style="text-align: center">3.2 sec</td>
<td style="text-align: right">2.0sec</td>
</tr>
<tr>
<td>Time to interactive</td>
<td style="text-align: center">19.5sec</td>
<td style="text-align: center">3.3sec</td>
<td style="text-align: right">2.0sec</td>
</tr>
<tr>
<td>JS / Bin size uncompressed</td>
<td style="text-align: center">2100KB</td>
<td style="text-align: center">210KB</td>
<td style="text-align: right">628KB</td>
</tr>
</tbody>
</table>
<ul>
<li>The Blazor client-side size is not enormous (<a href="https://discuss.httparchive.org/t/tracking-page-weight-over-time/1049">as of July ‘17 the average page size on the internet is 3MB</a>) but still it’s 3-4x bigger than the Angular app and 10x the Blazor server-side size. The team is working on reducing this (1.5MB is the tartget for the release). But still you will have to be careful with the library your are using on your front-end project, just like you would do with Angular.</li>
<li>The time to interactive is quite high for the Blazor client-side, maybe the reduction of binary size or the rise of AoT will solve this.</li>
<li>Blazor server-side performance are close to Angular</li>
</ul>
<p>Note : This test doesn’t use gzip compression for the web server so you can reduce the Blazor ouput by 40% and Angular by 60% (from my experience).</p>
<h4 id="results-with-cpu-and-bandwidth-throtling-off">Results with CPU and bandwidth throtling off</h4>
<table>
<thead>
<tr>
<th>Metric</th>
<th style="text-align: center">Blazor Client Side</th>
<th style="text-align: center">Blazor Server Side</th>
<th style="text-align: right">Angular</th>
</tr>
</thead>
<tbody>
<tr>
<td>Performance score</td>
<td style="text-align: center">100</td>
<td style="text-align: center">100</td>
<td style="text-align: right">98</td>
</tr>
<tr>
<td>First contentful paint</td>
<td style="text-align: center">0.2sec</td>
<td style="text-align: center">0.2sec</td>
<td style="text-align: right">2.0sec</td>
</tr>
<tr>
<td>Time to interactive</td>
<td style="text-align: center">1.6sec</td>
<td style="text-align: center">0.2sec</td>
<td style="text-align: right">2.0sec</td>
</tr>
<tr>
<td>JS / Bin size uncompressed</td>
<td style="text-align: center">2100KB</td>
<td style="text-align: center">210KB</td>
<td style="text-align: right">628KB</td>
</tr>
</tbody>
</table>
<ul>
<li>Blazor client-side performs better with better CPU and better bandwidth (no sh#t !), the Time to Interactive is now acceptable.</li>
<li>Blazor server-side performance are better than Angular</li>
</ul>
<p>Results : You have to decide, depending on the kind of hardware your user are running on (maybe you are creating an intranet for a very rich lawyer and they all have great hardware or it’s a public school and they didn’t change anything since 85), the bandwidth and the kind of UX you need to provide (20 sec to wait at the beginning of each day is not a lot). Performance IS a requirement, ask your stakeholder what they need. Blazor server-side seems to be performing very well.</p>
<h3 id="do-we-need-to-setup-some-training-for-blazor-">Do we need to setup some training for Blazor ?</h3>
<p>If your team already uses ASPNET Core MVC or Razor Pages, there isn’t a lot of things to learn. If you already know ASPNET, you will feel like it’s just an other library that will enable a lot of things. From a tooling perspective you can still use Visual Studio, nuget, dotnet cli, Azure DevOps, your favorite lib …</p>
<p>Resuts : that’s a HUGE selling point for Blazor. ASPNET Core is now a full stack framework, you don’t need multiple skills or development environment for building a Web Application.</p>
<h3 id="how-long-will-it-take-to-set-our-servers-up-">How long will it take to set our servers up ?</h3>
<p>Blazor Client-side : there is no additionnal setup to do if you use the hosted template because your ASPNET app will embed the client-side binaries and all the necessary configurations (mime type setup …). If you need to publish your app in a standalone app then you can read the instructions <a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/blazor/webassembly?view=aspnetcore-3.1">here</a>.</p>
<p>Blazor Server-side : You need to setup WebSocket because it’s the most efficient way to handle SignalR connection. You can find more informations <a href="https://docs.microsoft.com/en-us/aspnet/core/host-and-deploy/blazor/server?view=aspnetcore-3.1">here</a>.</p>
<p>Resuts : that’s again a nice selling point for Blazor, it will change nearly nothing to your deployment scripts and server setup.</p>
<h3 id="is-blazor-production-ready-">Is Blazor production ready ?</h3>
<p>Blazor client-side :</p>
<ul>
<li><a href="https://github.com/dotnet/aspnetcore/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue++label%3Ablazor-wasm+">There is currently</a> 67 Opened issues (12 bugs) on the ASPNET repo. You can browse the opened one and see if they are showstopper for you. The only thing that might be a showstopper is this <a href="https://github.com/dotnet/aspnetcore/issues/5477">one</a>, where some people reported that their firewall blocked download of client-side binaries because their url ended with “.dll”. It’s planned to be fixed on 3.2 preview4 which is planned for April (before the first release in May).</li>
<li><strong>Blazor client-side was not released yet</strong>, you might want to wait before using it in production. But it shares a lot with Blazor server-side which was released 4 month ago, the team will certainy not published a lot of breaking changes in the syntax or the framework mechanism.</li>
</ul>
<p>Blazor server-side :</p>
<ul>
<li><a href="https://github.com/dotnet/aspnetcore/issues?utf8=%E2%9C%93&q=is%3Aopen+is%3Aissue++label%3Aarea-blazor+">There is currently</a> 453 Opened issues (87 bugs) on the ASPNET repo. You can browse the opened one and see if there are showstopper for you.</li>
</ul>
<p>Results : if you prefer Blazor client-side, then I suggest you wait until May for the release. For Blazor server side, it is production ready and used (<a href="https://channel9.msdn.com/Events/dotnetConf/Focus-on-Blazor/Welcome-to-Blazor">see some example here</a>).</p>
<h3 id="is-it-compatible-with-most-browsers-">Is it compatible with most browsers ?</h3>
<p>Blazor client-side :</p>
<ul>
<li><a href="https://caniuse.com/#feat=wasm">88,53%</a> os the internet user base uses a browser that can execute WebAssembly.</li>
<li>Given the output size, you need a decent bandwidth for having a nice user experience (2MB takes 8sec to download on a 2Mbps connection, <a href="https://en.wikipedia.org/wiki/List_of_countries_by_Internet_connection_speeds">Avg worldwide bandwidth is 5.1Mbps</a>).</li>
<li>Like any client-side executed code, the user experience is dependant on the user CPU. Don’t forget to test your app with slow CPU to be sure that the user experience is still OK. I can’t find any data about the average CPU in the world, so you might need to add some APM to your app, like Application Insight, that will centralize browser loading time so you have a clue of the user experience on your app.</li>
</ul>
<p>Blazor server-side :</p>
<ul>
<li>it’s compatible with almost any browser as <a href="https://docs.microsoft.com/en-us/aspnet/signalr/overview/getting-started/introduction-to-signalr">it uses many connection technique</a> (long polling, WebSocket) and choose the best available for your browser.</li>
</ul>
<p>Results : like with performance, that’s your call. For me the size argument is relevant only if you are targeting mobile users.</p>
<h3 id="can-it-open-new-opportunities-">Can it open new opportunities ?</h3>
<p>Yes hundred times. Blazor seems to be the next UI framework for Microsoft as <a href="https://channel9.msdn.com/Events/dotnetConf/Focus-on-Blazor/Blazor-Futures-WebAssembly-PWAs-Hybrid-Native">they are working</a> for desktop and mobile app, so in the near future you will be able to build a full application for any environment with .NET core and Blazor.</p>
<p>Results : that’s a good point for Blazor.</p>
<h3 id="will-we-develop-faster-with-blazor-">Will we develop faster with Blazor ?</h3>
<p>That’s highly subjective but I can give you a few arguments :</p>
<ul>
<li>The fact that you can share code between client and server will greatly improve your productivity. Imagine this : you code the same validation for client-side or server-side, the object you exchange between the client and server are defined only once, even your constants can be shared :D.</li>
<li>The fact you use only one IDE has a few advantages.</li>
<li>The fact that you will use one syntax accross your fullstack will improve onboarding of unexperienced developer.</li>
<li>Refactoring becomes way easier (for instance renaming a field)</li>
<li>You can debug both type of app just like you would do with a MEAN stack : on the browser for client-side code, and on the server for server-side. But this will change before the Blazor client-side release in May as you should be able to debug it in Visual Studio as well.</li>
<li>The fact that the deployment story doesn’t change will make your DevOps work more on the Dev than on learning how to setup nginx or build a production angular app.</li>
<li>When you need some JS function or Browser API it will need a bit more work.</li>
</ul>
<p>Results : after reading this you should have decided to use Blazor :D</p>
<h3 id="should-we-use-blazor-with-our-nodegospringetc-backend-">Should we use Blazor with our Node/Go/Spring/etc backend ?</h3>
<p>Well that a tough one. Honestly I don’t know, the point of Blazor is mostly its integration in the full .NET ecosystem, using it with Spring seems pointless. BUT I have been doing some Angular developement for a while and honestly I prefer Razor / C# syntax to Type Script and Angular templating. But here it’s a matter of taste and there isn’t any objective way for answering.</p>
<p>Results : there is no reason to not use it, see with your team members. If they like Java maybe they will prefer Blazor to Angular or React.</p>
<h2 id="conclusion">Conclusion</h2>
<p>I can see one situation where the choice would be obvious for me : an ASPNET Core web APP (not web site), I would do the GUI with Blazor. I would pick server-side if the connection problem does not apply, client-side else.</p>
<p>On every other case I would follow my question list and do the choice with the team. But still I beleive in this project and I believe it will be around for a long time.</p>
<p>We often compare framework and library with hammer and screwdriver while this is not wrong, there is no right choice. Angular, React, Blazor, Vue are different brands of screwdriver, there is some difference, but most of the time the decision is based on feelings, taste and experience.</p>Should I peek Blazor ?Make Your Blazor Development Faster2019-11-08T00:00:00+00:002019-11-08T00:00:00+00:00https://remibou.github.io/Make-your-Blazor-development-faster<h1 id="how-to-improve-your-development-experience-with-blazor">How to improve your development experience with Blazor</h1>
<p>At first Blazor development can be a bit slow. When doing a change on a razor file you need to do the following steps :</p>
<ul>
<li>rebuild your front end project</li>
<li>rebuild the backend</li>
<li>restart your backend</li>
<li>refresh the browser</li>
<li>go back to your work</li>
</ul>
<p>In this article I’ll explore a few things for improving life of Blazor developers.</p>
<h2 id="improve-build-time-by-disabling-illinker">Improve build time by disabling Illinker</h2>
<p>The Illinker is a great tool as it removes all the unused things from the dll files produced for your project. The second advantage being that it will find diamond dependency problem (dependency A and B depends on different version of dependency C). But it slows your build down : for my <a href="https://github.com/RemiBou/Toss.Blazor">Toss project</a> it increases the build time by 766% (3sec -> 23sec) !! If I build my project 100 times per day, I lose 33 minutes only in build time every day.</p>
<p>It seems that the documentation is not right about the linker and it runs even when you are in Debug build configuration. You need to add the following lines to your client project for disabling it :</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><PropertyGroup></span>
<span class="nt"><BlazorLinkOnBuild</span> <span class="na">Condition=</span><span class="s">"'$(Configuration)'!='Release'"</span><span class="nt">></span>false<span class="nt"></BlazorLinkOnBuild></span>
<span class="nt"></PropertyGroup></span>
</code></pre></div></div>
<p>With this the linker will be enabled when you build your project with “-c Release” on your build run or publish dotnet command and disabled else.</p>
<h2 id="auto-rebuild-of-backend-project">Auto rebuild of backend project</h2>
<p>Despite all the unit/integration/e2e test you can do, you need to actually see your GUI for validating it. This involves a lot of back and forth between your code and the browser. Right now when you change your razor file content, you need to build the backend project so it has the latest version of your blazor project dll. This can take some time and can also lead to stupid debugging session during which you try to find out why your changes doesn’t work until you realize you forgot to build your project (don’t deny it, it happened to all of us).</p>
<p>Right now for running your project, you cd onto your backend project and hit :</p>
<pre><code class="language-cmd">dotnet run
</code></pre>
<p>We can improve this by using the watch subcommand, so the project will be automatically rebuild when we change a .cs file</p>
<pre><code class="language-cmd">dotnet watch run
</code></pre>
<p>The problem is, the rebuild won’t occur if you change a razor file on your client project. To do so, you need to add to following lines to your backend project csproj :</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nt"><ItemGroup></span>
<span class="c"><!-- extends watching group to include *.razor files --></span>
<span class="nt"><Watch</span> <span class="na">Include=</span><span class="s">"..\ClientProject\**\*.razor"</span> <span class="nt">/></span>
<span class="nt"></ItemGroup></span>
</code></pre></div></div>
<ul>
<li>ClientProject is your client project folder</li>
<li>Now “dotnet watch run” will rebuild both (front and back) projects when you change a razor file</li>
</ul>
<h2 id="refreshing-browser-windows-when-rebuilding">Refreshing browser windows when rebuilding</h2>
<p>It would also be nice to refresh the browser when a change occurs, a bit like with “ng serve”. With this you would be nearly sure that you are always executing the latest version of your code. For this <a href="https://weblog.west-wind.com/">Rick Strahl</a> created a package named <a href="https://github.com/RickStrahl/Westwind.AspnetCore.LiveReload">Westwind.AspnetCore.LiveReload</a>. But this package has a few bugs or is not working correctly for me, so I decided to fork it and change it for my usage. You can use the originalpackage from nuget or use my fork. For using my fork you can execute this git commands</p>
<pre><code class="language-cmd">git submodule add -b RemiBou-better-refresh https://github.com/RemiBou/Westwind.AspnetCore.LiveReload
</code></pre>
<p>Then on your backend project file add the following project reference</p>
<div class="language-xml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="nt"><ProjectReference</span> <span class="na">Include=</span><span class="s">"..\Westwind.AspnetCore.LiveReload\Westwind.AspnetCore.LiveReload\Westwind.AspNetCore.LiveReload.csproj"</span> <span class="nt">/></span>
</code></pre></div></div>
<p>Now in your Startup.cs file add this line to ConfigureServices method</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">services</span><span class="p">.</span><span class="nf">AddLiveReload</span><span class="p">(</span><span class="n">config</span> <span class="p">=></span> <span class="p">{</span>
<span class="n">config</span><span class="p">.</span><span class="n">LiveReloadEnabled</span> <span class="p">=</span> <span class="k">true</span><span class="p">;</span>
<span class="n">config</span><span class="p">.</span><span class="n">ClientFileExtensions</span> <span class="p">=</span> <span class="s">".css,.js,.htm,.html"</span><span class="p">;</span>
<span class="n">config</span><span class="p">.</span><span class="n">FolderToMonitor</span> <span class="p">=</span> <span class="s">"~/../"</span><span class="p">;</span>
<span class="p">});</span>
</code></pre></div></div>
<p>And in your Configure method (before any middleware related to static files or to blazor).</p>
<div class="language-cs highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="n">app</span><span class="p">.</span><span class="nf">UseLiveReload</span><span class="p">();</span>
</code></pre></div></div>
<p>This will do 2 things :</p>
<ul>
<li>Create a websocket that send a message when a file matching the ClientFileExtensions is changed, so when you change a .html or a css file, the page will be automaticaly reloaded</li>
<li>Change every outgoing html file with the LiveReload scripts that connects to the websocket, listen for change and execute the page refresh. It will also detect disconnection, try to reconnect and reload the page when the reconnection succeeds.</li>
</ul>
<p>Now you can test it, run “dotnet watch run” on your backend project.</p>
<ul>
<li>If it is a .cs or a .razor the server will be shutdown, the project build, the server started and your page reloaded automatically</li>
<li>If it is a .html, a .css or a .js, the page will be reloaded without the need for compilation.</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>The developer experience when using a framework is very important. The more manual and repititive task you can remove the more efficient your team is. I hope the aspnet team will work on built-in feature for making this easier.</p>How to improve your development experience with BlazorTest A Blazor App With Cypress2019-10-18T00:00:00+00:002019-10-18T00:00:00+00:00https://remibou.github.io/Test-a-Blazor-App-with-Cypress<h1 id="how-to-test-a-blazor-app-with-cypress-using-docker-compose">How to test a Blazor app with Cypress using docker-compose</h1>
<p>On my <a href="https://github.com/RemiBou/Toss.Blazor">Toss project</a>, I chose to have some end-to-end (e2e). End-to-end test on web project are tests that automate a browsing session on a web browser. Most of the time it works by using API provided by an existing browser (like chrome). Those kind of tests have many drawbacks :</p>
<ul>
<li>Force you to add ids everywhere on your html code so you can find element on your test code</li>
<li>Are often flaky because some load time might vary between two test run or you can change your front-end code without thinking about the changes needed in the test</li>
<li>Are hard to build first because you can forget some steps, like scroll down or click here.</li>
</ul>
<p>But they are important for making sure that your app works and you don’t have a huge error when your app startup because you miss a js reference or something else.</p>
<p>At first I started my tests with Selenium. Mainly because it’s the most common way to do this and because there was some example on the aspnet core repo. But Selenium tests are hard to write and very flaky (just look at my <a href="https://github.com/RemiBou/Toss.Blazor/commits/master">git logs</a>) for many reasons :</p>
<ul>
<li>An element must be displayed on the screen before accepting interactions, so you must code the scrolling up/down, size screen etc …</li>
<li>You can only use the browser like a normal user so you can’t add code for waiting the end of an http query or check the status of a xhr.</li>
<li>There isn’t much debugging information provided : screenshot are hard to get and videos impossible.</li>
</ul>
<p>I heard a lot of good thing about <a href="https://www.cypress.io/">cypress</a> so I decided to give it a shot after an unsuccessful battle against Selenium.</p>
<h2 id="start-the-application-and-dependencies">Start the application and dependencies</h2>
<p>Because my previous E2E test used Selenium it was a .NET Core project. This was usefull for launching RavenEB.Embedded. Cypress running on the browser runtime, I won’t have this project anymore. I needed an other way for starting my ravendb server : so I picked docker-compose, which I could use for starting my whole system : my app and the dependencies.
I really like docker and using docker-compose for running E2E tests seems like a good idea. For running my app in a docker-compose I need to build a docker image for it, here it is :</p>
<div class="language-dockerfile highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">FROM</span><span class="s"> mcr.microsoft.com/dotnet/core/runtime:2.2.7-alpine as runtime227</span>
<span class="k">FROM</span><span class="s"> mcr.microsoft.com/dotnet/core/sdk:3.0.100-alpine AS build</span>
<span class="c"># import sdk from 2.2.7 because we need it for running ravendb embedded</span>
<span class="k">COPY</span><span class="s"> --from=runtime227 /usr/share/dotnet /usr/share/dotnet </span>
<span class="k">WORKDIR</span><span class="s"> /src</span>
<span class="k">COPY</span><span class="s"> ./Toss.Client/Toss.Client.csproj ./Toss.Client/</span>
<span class="k">COPY</span><span class="s"> ./Toss.Server/Toss.Server.csproj ./Toss.Server/</span>
<span class="k">COPY</span><span class="s"> ./Toss.Shared/Toss.Shared.csproj ./Toss.Shared/</span>
<span class="k">COPY</span><span class="s"> ./Toss.Tests/Toss.Tests.csproj ./Toss.Tests/</span>
<span class="k">COPY</span><span class="s"> ./Toss.sln ./</span>
<span class="k">RUN </span>dotnet restore ./Toss.sln
<span class="k">COPY</span><span class="s"> ./Toss.Client ./Toss.Client</span>
<span class="k">COPY</span><span class="s"> ./Toss.Server ./Toss.Server</span>
<span class="k">COPY</span><span class="s"> ./Toss.Shared ./Toss.Shared</span>
<span class="k">COPY</span><span class="s"> ./Toss.Tests ./Toss.Tests</span>
<span class="k">RUN </span>dotnet <span class="nb">test</span> ./Toss.Tests
<span class="k">RUN </span>dotnet publish Toss.Server/Toss.Server.csproj <span class="nt">-c</span> Release <span class="nt">-o</span> /app
<span class="k">FROM</span><span class="s"> mcr.microsoft.com/dotnet/core/aspnet:3.0</span>
<span class="k">WORKDIR</span><span class="s"> /app</span>
<span class="k">COPY</span><span class="s"> --from=build /app .</span>
<span class="k">EXPOSE</span><span class="s"> 80</span>
<span class="k">ENTRYPOINT</span><span class="s"> ["dotnet", "Toss.Server.dll"]</span>
</code></pre></div></div>
<ul>
<li>We can see a really nice feature of Docker : I was able to import the 2.2.7 runtime into a 3.0 sdk docker image . I needed this because my integration tests runs RavenDB.Embedded which is not compatible with runtime 3.</li>
<li>With multi stage build I can use the sdk for building my app then only the runtime for executing it which will make my final docker image smaller</li>
<li>I first copy the csproj and sln files for optimizing layer caching : if I don’t change anything to my csproj files, then the “RUN dotnet restore ./Toss.sln” line will use the cached version of this layer, making my build faster (even though Azure DevOps doesn’t provide docker layer caching yet).</li>
<li>“RUN dotnet test ./Toss.Tests” runs my integration tests, not the E2E.</li>
</ul>
<p>Then I need to create a docker-compose file describing my app and its dependencies :</p>
<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">version</span><span class="pi">:</span> <span class="s1">'</span><span class="s">3.5'</span>
<span class="na">services</span><span class="pi">:</span>
<span class="na">web</span><span class="pi">:</span>
<span class="na">build</span><span class="pi">:</span>
<span class="na">context</span><span class="pi">:</span> <span class="s">.</span>
<span class="na">restart</span><span class="pi">:</span> <span class="s">always</span>
<span class="na">environment</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">GoogleClientId=AAA</span>
<span class="pi">-</span> <span class="s">GoogleClientSecret=AAA</span>
<span class="pi">-</span> <span class="s">MailJetApiKey=</span>
<span class="pi">-</span> <span class="s">MailJetApiSecret=</span>
<span class="pi">-</span> <span class="s">MailJetSender=</span>
<span class="pi">-</span> <span class="s">RavenDBEndpoint=http://ravendb:8080</span>
<span class="pi">-</span> <span class="s">RavenDBDataBase=Tests</span>
<span class="pi">-</span> <span class="s">StripeSecretKey=</span>
<span class="pi">-</span> <span class="s">test=true</span>
<span class="na">depends_on</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">ravendb</span>
<span class="na">ports</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">80:80</span>
<span class="na">ravendb</span><span class="pi">:</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">ravendb/ravendb</span>
</code></pre></div></div>
<ul>
<li>I expose the port 80 so I can access the app in my dev computer</li>
<li>“test=true” tells my app to use fake dependencies for things like external services (mailjet, stripe) or non deterministic data (random, datetime.now)</li>
<li>The good thing with docker-compose is that the services run on their own network and are accessible inside this network accessible via their name, so I can run ravendb inside this network and it won’t have an impact on the other ravendb I might be running on my dev computer.</li>
</ul>
<p>Now I can run my app with the following command :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose up <span class="nt">-d</span>
</code></pre></div></div>
<p>Maybe I’ll use it as a starting point for hosting my app in production but it is not the point right now.</p>
<h2 id="cypress-test">Cypress Test</h2>
<p>First I create the folder “Toss.Tests.E2E.Cypress” in my solution then cd on it. You need npm installed on your computer. Then type on your terminal :</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>npm init
npm <span class="nb">install </span>cypress
</code></pre></div></div>
<p>Once installed I got a cypress folder containing multiple folder, I cleaned up all the samples in the folder called “integration”. My new test will be a js file inside this integration folder :</p>
<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">/// <reference types="Cypress" /></span>
<span class="nx">describe</span><span class="p">(</span><span class="dl">'</span><span class="s1">Toss Full Test</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="kd">let</span> <span class="nx">polyfill</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">uuid</span> <span class="o">=</span> <span class="nx">Cypress</span><span class="p">.</span><span class="nx">_</span><span class="p">.</span><span class="nx">random</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">1</span><span class="nx">e6</span><span class="p">)</span>
<span class="nx">before</span><span class="p">(()</span> <span class="o">=></span> <span class="p">{</span>
<span class="kd">const</span> <span class="nx">polyfillUrl</span> <span class="o">=</span> <span class="dl">'</span><span class="s1">https://unpkg.com/whatwg-fetch@3.0.0/dist/fetch.umd.js</span><span class="dl">'</span><span class="p">;</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">request</span><span class="p">(</span><span class="nx">polyfillUrl</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">polyfill</span> <span class="o">=</span> <span class="nx">response</span><span class="p">.</span><span class="nx">body</span><span class="p">;</span>
<span class="p">});</span>
<span class="p">});</span>
<span class="nx">Cypress</span><span class="p">.</span><span class="nx">on</span><span class="p">(</span><span class="dl">'</span><span class="s1">window:before:load</span><span class="dl">'</span><span class="p">,</span> <span class="nx">win</span> <span class="o">=></span> <span class="p">{</span>
<span class="k">delete</span> <span class="nx">win</span><span class="p">.</span><span class="nx">fetch</span><span class="p">;</span>
<span class="nx">win</span><span class="p">.</span><span class="nb">eval</span><span class="p">(</span><span class="nx">polyfill</span><span class="p">);</span>
<span class="p">});</span>
<span class="kd">const</span> <span class="nx">SubscribeEmail</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">tosstests</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">uuid</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">@yopmail.com</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">SubscribePassword</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">tossTests123456!!</span><span class="dl">"</span><span class="p">;</span>
<span class="kd">const</span> <span class="nx">SubscribeLogin</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">tosstests</span><span class="dl">"</span> <span class="o">+</span> <span class="nx">uuid</span><span class="p">;</span>
<span class="nx">it</span><span class="p">(</span><span class="dl">'</span><span class="s1">Full process</span><span class="dl">'</span><span class="p">,</span> <span class="kd">function</span> <span class="p">()</span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">server</span><span class="p">();</span>
<span class="c1">//used for listenning to register api call and getting the recirction url from the http headers</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">route</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">/api/account/register</span><span class="dl">'</span><span class="p">).</span><span class="k">as</span><span class="p">(</span><span class="dl">'</span><span class="s1">register</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">route</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">/api/account/login</span><span class="dl">'</span><span class="p">).</span><span class="k">as</span><span class="p">(</span><span class="dl">'</span><span class="s1">login</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">route</span><span class="p">(</span><span class="dl">'</span><span class="s1">POST</span><span class="dl">'</span><span class="p">,</span> <span class="dl">'</span><span class="s1">/api/toss/create</span><span class="dl">'</span><span class="p">).</span><span class="k">as</span><span class="p">(</span><span class="dl">'</span><span class="s1">create</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">visit</span><span class="p">(</span><span class="dl">"</span><span class="s2">/</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">disableCaptcha</span><span class="p">();</span>
<span class="c1">//this could be long as ravendb is starting</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#LinkLogin</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">timeout</span><span class="p">:</span> <span class="mi">20000</span> <span class="p">}).</span><span class="nx">click</span><span class="p">();</span>
<span class="c1">//register</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#LinkRegister</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#NewEmail</span><span class="dl">"</span><span class="p">).</span><span class="nx">type</span><span class="p">(</span><span class="nx">SubscribeEmail</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#NewName</span><span class="dl">"</span><span class="p">).</span><span class="nx">type</span><span class="p">(</span><span class="nx">SubscribeLogin</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#NewPassword</span><span class="dl">"</span><span class="p">).</span><span class="nx">type</span><span class="p">(</span><span class="nx">SubscribePassword</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#NewConfirmPassword</span><span class="dl">"</span><span class="p">).</span><span class="nx">type</span><span class="p">(</span><span class="nx">SubscribePassword</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#BtnRegister</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">wait</span><span class="p">(</span><span class="dl">'</span><span class="s1">@register</span><span class="dl">'</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">'</span><span class="s1">@register</span><span class="dl">'</span><span class="p">).</span><span class="nx">then</span><span class="p">(</span><span class="kd">function</span> <span class="p">(</span><span class="nx">xhr</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">status</span><span class="p">).</span><span class="nx">to</span><span class="p">.</span><span class="nx">eq</span><span class="p">(</span><span class="mi">200</span><span class="p">);</span>
<span class="nx">expect</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">headers</span><span class="p">[</span><span class="dl">'</span><span class="s1">x-test-confirmationlink</span><span class="dl">'</span><span class="p">]).</span><span class="nx">to</span><span class="p">.</span><span class="nx">not</span><span class="p">.</span><span class="nx">empty</span><span class="p">;</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">"</span><span class="s2">Redirect URL : </span><span class="dl">"</span> <span class="o">+</span> <span class="nx">xhr</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">headers</span><span class="p">[</span><span class="dl">'</span><span class="s1">x-test-confirmationlink</span><span class="dl">'</span><span class="p">]);</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">visit</span><span class="p">(</span><span class="nx">xhr</span><span class="p">.</span><span class="nx">response</span><span class="p">.</span><span class="nx">headers</span><span class="p">[</span><span class="dl">'</span><span class="s1">x-test-confirmationlink</span><span class="dl">'</span><span class="p">]);</span>
<span class="nx">disableCaptcha</span><span class="p">();</span>
<span class="c1">//login</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#UserName</span><span class="dl">"</span><span class="p">,</span> <span class="p">{</span> <span class="na">timeout</span><span class="p">:</span> <span class="mi">20000</span> <span class="p">}).</span><span class="nx">type</span><span class="p">(</span><span class="nx">SubscribeEmail</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#Password</span><span class="dl">"</span><span class="p">).</span><span class="nx">type</span><span class="p">(</span><span class="nx">SubscribePassword</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#BtnLogin</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">wait</span><span class="p">(</span><span class="dl">'</span><span class="s1">@login</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">//publish toss</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#LinkNewToss</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">newTossContent</span> <span class="o">=</span> <span class="dl">"</span><span class="s2">lorem ipsum lorem ipsumlorem ipsum lorem ipsumlorem ipsum lorem ipsumlorem ipsum lorem ipsum #test</span><span class="dl">"</span><span class="p">;</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#TxtNewToss</span><span class="dl">"</span><span class="p">).</span><span class="nx">type</span><span class="p">(</span><span class="nx">newTossContent</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#BtnNewToss</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">wait</span><span class="p">(</span><span class="dl">'</span><span class="s1">@create</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">//publish toss x2</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#LinkNewToss</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="kd">var</span> <span class="nx">newTossContent2</span> <span class="o">=</span> <span class="dl">"</span><span class="s2"> lorem ipsum lorem ipsumlorem ipsum lorem ipsumlorem ipsum lorem ipsumlorem ipsum lorem ipsum #toto</span><span class="dl">"</span><span class="p">;</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#TxtNewToss</span><span class="dl">"</span><span class="p">).</span><span class="nx">type</span><span class="p">(</span><span class="nx">newTossContent2</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#BtnNewToss</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">wait</span><span class="p">(</span><span class="dl">'</span><span class="s1">@create</span><span class="dl">'</span><span class="p">);</span>
<span class="c1">//add new hashtag</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#TxtAddHashTag</span><span class="dl">"</span><span class="p">).</span><span class="nx">type</span><span class="p">(</span><span class="dl">"</span><span class="s2">test</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#TxtAddHashTag</span><span class="dl">"</span><span class="p">).</span><span class="nx">type</span><span class="p">(</span><span class="dl">"</span><span class="s2">{enter}</span><span class="dl">"</span><span class="p">);</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#BtnAddHashTag</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">.toss-preview</span><span class="dl">"</span><span class="p">).</span><span class="nx">first</span><span class="p">().</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">.toss-detail .toss-content</span><span class="dl">"</span><span class="p">).</span><span class="nx">should</span><span class="p">(</span><span class="dl">"</span><span class="s2">contain</span><span class="dl">"</span><span class="p">,</span> <span class="nx">newTossContent</span><span class="p">);</span>
<span class="c1">// logout</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#LinkAccount</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">cy</span><span class="p">.</span><span class="kd">get</span><span class="p">(</span><span class="dl">"</span><span class="s2">#BtnLogout</span><span class="dl">"</span><span class="p">).</span><span class="nx">click</span><span class="p">();</span>
<span class="nx">cy</span><span class="p">.</span><span class="nx">url</span><span class="p">().</span><span class="nx">should</span><span class="p">(</span><span class="dl">"</span><span class="s2">eq</span><span class="dl">"</span><span class="p">,</span> <span class="nx">Cypress</span><span class="p">.</span><span class="nx">config</span><span class="p">().</span><span class="nx">baseUrl</span> <span class="o">+</span> <span class="dl">"</span><span class="s2">/</span><span class="dl">"</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">})</span>
<span class="p">})</span>
<span class="kd">function</span> <span class="nx">disableCaptcha</span><span class="p">()</span> <span class="p">{</span>
<span class="nx">cy</span><span class="p">.</span><span class="nb">window</span><span class="p">()</span>
<span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">win</span> <span class="o">=></span> <span class="p">{</span>
<span class="nx">win</span><span class="p">.</span><span class="nx">runCaptcha</span> <span class="o">=</span> <span class="k">new</span> <span class="nx">win</span><span class="p">.</span><span class="nb">Function</span><span class="p">([</span><span class="dl">'</span><span class="s1">action</span><span class="dl">'</span><span class="p">],</span> <span class="dl">'</span><span class="s1">return Promise.resolve(action)</span><span class="dl">'</span><span class="p">);</span>
<span class="p">});</span>
<span class="p">}</span>
</code></pre></div></div>
<ul>
<li>Cypress uses mocha for running the tests, so your test fixture must be a call to “describe()” function and inside each test there is multiple calls to a “it” function. As in every test runner, there are hooks for running things before/after each/every tests.</li>
<li>Cypress is able to read every XHR request done by your site, but Blazor uses fetch for http call. We need to remove the current implementation of fetch and replace it by a polyfill that uses xhr.</li>
<li>We can see with the route() and wait() call how you can wait until an http request is done and how we can read its content : here I needed a way to get the confirmation link after a subscribtion, so server side I send this link in the http response header (only in test mode) and I read it on client side and then visit() the link.</li>
<li>For E2E I prefer to write one big test that does a lot of thing, it’s just a mater of taste. I don’t practice TDD with E2E test it would be too hard, I prefer to create it when the development is done just for making sure it will keep working after my future updates.</li>
<li>The method disableCaptcha is used for disabling recaptcha. Be careful here, I create the method with a “new win.Function” for a specific reason. When you do interop C# -> js, Microsoft.JSINterop (used by Blazor) <a href="https://github.com/aspnet/Extensions/blob/master/src/JSInterop/Microsoft.JSInterop.JS/src/src/Microsoft.JSInterop.ts">check that the argument send is a function</a> with this code</li>
</ul>
<div class="language-ts highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nx">result</span> <span class="k">instanceof</span> <span class="nb">Function</span><span class="p">)</span> <span class="p">{</span>
<span class="nx">result</span> <span class="o">=</span> <span class="nx">result</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="nx">lastSegmentValue</span><span class="p">);</span>
<span class="nx">cachedJSFunctions</span><span class="p">[</span><span class="nx">identifier</span><span class="p">]</span> <span class="o">=</span> <span class="nx">result</span><span class="p">;</span>
<span class="k">return</span> <span class="nx">result</span><span class="p">;</span>
<span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
<span class="k">throw</span> <span class="k">new</span> <span class="nb">Error</span><span class="p">(</span><span class="s2">`The value '</span><span class="p">${</span><span class="nx">resultIdentifier</span><span class="p">}</span><span class="s2">' is not a function.`</span><span class="p">);</span>
<span class="p">}</span>
</code></pre></div></div>
<p>Here is the catch : on my test if I write “win.runCaptcha = function(action){return Promise.resolve(action);}” it would throw an error “The value ‘window.runCaptcha’ is not a function.” because class definition (like Function) are namespaced by window so my method would be a function in the namespace of the cypress window, not on the namespace of the tested app window (it took me no less than 2 days to figure it out).</p>
<p>Now for testing this I cd on the test directory and enter</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>./node_modules/.bin/cypress open
</code></pre></div></div>
<p>Which opens a web UI where I can run my test and see them while they are executing. This GUI is really nice because you can go in the past and see the state of the UI or even browse the DOM. Now that my test passes I want to integrate cypress into my docker-compose so I can run it without installing cypress, which will help when I’ll run it in my CI environment.</p>
<h2 id="integrate-cypress-into-the-docker-compose">Integrate Cypress into the docker-compose</h2>
<p>I added the following service to my docker-compose.yml</p>
<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="na">cypress</span><span class="pi">:</span>
<span class="na">image</span><span class="pi">:</span> <span class="s">cypress/included:3.4.1</span>
<span class="na">depends_on</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">web</span>
<span class="na">environment</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">CYPRESS_baseUrl=http://web</span>
<span class="na">working_dir</span><span class="pi">:</span> <span class="s">/e2e</span>
<span class="na">volumes</span><span class="pi">:</span>
<span class="pi">-</span> <span class="s">./Toss.Tests.E2E.Cypress/:/e2e</span>
</code></pre></div></div>
<ul>
<li>I mount the cypress tests as a volume, so I can also get the cypress test execution artifacts back (screenshot and video)</li>
<li>The “depends_on” is really usefull for starting things in order (ravendb -> web -> cypress)</li>
<li>By default cypress tries to test http://localhost, here I change it for the service url with the env variable CYPRESS_baseUrl</li>
</ul>
<p>Then for running the test I run the following command from the root fo my project</p>
<div class="language-bash highlighter-rouge"><div class="highlight"><pre class="highlight"><code>docker-compose up <span class="nt">--renew-anon-volumes</span> <span class="nt">--exit-code-from</span> cypress <span class="nt">--build</span>
</code></pre></div></div>
<ul>
<li>”–renew-anon-volumes” is used for cleaning ravendb volumes before each run, so I start with an empty DB.</li>
<li>”–exit-code-from cypress” is used for telling docker-compose to kill all the other services when the service “cypress” is done and it’ll use the cypress return code as its own.</li>
<li>”–build” will build our service with build specification (only “web”)</li>
</ul>
<h2 id="integration-on-azure-devops">Integration on Azure DevOps</h2>
<p>Now that I got a fully running integration I can replace the existing one for this. Because it uses docker-compose it’ll be easier to maintain (I can test it on my dev machine and on multiple OS) and will also help for running other servcies. Here is my new “azure-pipelines.yml” file</p>
<div class="language-yml highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="na">pool</span><span class="pi">:</span>
<span class="na">name</span><span class="pi">:</span> <span class="s">Azure Pipelines</span>
<span class="na">vmImage</span><span class="pi">:</span> <span class="s">ubuntu-16.04</span>
<span class="na">steps</span><span class="pi">:</span>
<span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">DockerCompose@0</span>
<span class="na">displayName</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Run</span><span class="nv"> </span><span class="s">a</span><span class="nv"> </span><span class="s">docker-compose</span><span class="nv"> </span><span class="s">command'</span>
<span class="na">inputs</span><span class="pi">:</span>
<span class="na">containerregistrytype</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Container</span><span class="nv"> </span><span class="s">Registry'</span>
<span class="na">dockerRegistryEndpoint</span><span class="pi">:</span> <span class="s1">'</span><span class="s">dockerhub</span><span class="nv"> </span><span class="s">remibou'</span>
<span class="na">dockerComposeFile</span><span class="pi">:</span> <span class="s1">'</span><span class="s">docker-compose.yml'</span>
<span class="na">dockerComposeCommand</span><span class="pi">:</span> <span class="s1">'</span><span class="s">up</span><span class="nv"> </span><span class="s">--renew-anon-volumes</span><span class="nv"> </span><span class="s">--exit-code-from</span><span class="nv"> </span><span class="s">cypress</span><span class="nv"> </span><span class="s">--build'</span>
<span class="pi">-</span> <span class="na">task</span><span class="pi">:</span> <span class="s">PublishPipelineArtifact@1</span>
<span class="na">displayName</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Publish</span><span class="nv"> </span><span class="s">Pipeline</span><span class="nv"> </span><span class="s">Artifact'</span>
<span class="na">inputs</span><span class="pi">:</span>
<span class="na">targetPath</span><span class="pi">:</span> <span class="s">Toss.Tests.E2E.Cypress/cypress/videos/</span>
<span class="na">artifact</span><span class="pi">:</span> <span class="s1">'</span><span class="s">Cypress</span><span class="nv"> </span><span class="s">videos'</span>
<span class="na">condition</span><span class="pi">:</span> <span class="s">succeededOrFailed()</span>
</code></pre></div></div>
<ul>
<li>I use an ubuntu image because it’s lighter than windows and I don’t think I’ll run my app on windows server so it’s better to do my build and test on a linux OS.</li>
<li>You need to create a docker endpoint on Azure DevOps so it can login into docker hub for pulling public images</li>
<li>I publish the cypress video as artifact so I won’t have any problem for debugging it when it fails</li>
</ul>
<h2 id="conclusion">Conclusion</h2>
<p>I hope this new test suite will make my E2E tests less flaky and more enjoyable to maintain and improve.</p>
<p>I can also say that Cypress is waaaay better than Selenium in every possible way :</p>
<ul>
<li>installation : a single docker service or an npm package</li>
<li>development : no need to add wait everywhere and you can watch XHR requests</li>
<li>execution : a web UI or a single command line</li>
<li>debug. : on the web UI you can browse all the test execution and you have a video of the whole thing.</li>
</ul>How to test a Blazor app with Cypress using docker-compose On my Toss project, I chose to have some end-to-end (e2e). End-to-end test on web project are tests that automate a browsing session on a web browser. Most of the time it works by using API provided by an existing browser (like chrome). Those kind of tests have many drawbacks : Force you to add ids everywhere on your html code so you can find element on your test code Are often flaky because some load time might vary between two test run or you can change your front-end code without thinking about the changes needed in the test Are hard to build first because you can forget some steps, like scroll down or click here.