Web Assemblies and the Theory Behind Blazor
Back from the days of Java applets, flash and Silverlight, companies and developers alike have always dreamt of being able to run full blown applications inside the browser.
Enter Web Assemblies.
Web Assemblies ship with a light weight stack machine which is capable of running code that has been compiled to binary. Think of this as Byte Code for the Web. This is cool because with web assemblies you can run compiled languages like C++ inside your browser.
It’s developed in a W3C community group which tells you it's not proprietary; The community group behind web assemblies has representatives from almost all browsers.
Web Assemblies run in any browser, on any platform at almost native speed. If you want to know more about Web Assemblies you can go here.
And why are we talking about Web Assemblies in a post on Blazor? Because Blazor is .NET runtime built using Web Assemblies. This means I can now take Dotnet code and run it inside a browser.
You build .NET Apps or assemblies and ship them as Blazor Apps. The Dotnet code you write gets downloaded and runs on the Blazor runtime which is basically a web assemblies implementation. It has the ability to interact with the Dom extremely efficiently and even find out what changed in the DOM.
Alternately, you can have the same C# code run on the server and have it update the client side Dom using a Signal-R Connection. Any UI events that happen on the client side are sent to the server using Signal R. The Server captures these and runs the relevant server side code. When the server modifies the dom, Blazor calculates a diff, serializes that diff back to client and browser applies it to Dom.
Let's Try Out Blazor
Actually, Blazor has existed for sometime now; but what's interesting is that Blazor Server now ships with .NET Core 3.0 and is production ready. The ability to build completely client side apps in Blazor using Web Assembly is in still in preview through and will most likely ship in May 2020.
The tooling is seriously awesome and simple. The implementation is so neat that to pick up the basic concepts all you have to do is just generate a new project with it and the tooling stubs out a fully functional hello world sample you can learn from.
As a quick overview let's stub two Blazor Projects, one using Blazor Server and one using Web Assemblies and let's try to learn from the basic hello world examples the tooling generates. As always we'll use Visual Studio Code because it's free and let's us look under the hood to understand the tooling.
Blazor Server Example:
To generate a new project I fire:
dotnet new blazorserver -o serverexample
(Where serverexample happens to be the name of the project I want to stub out).
This stubs out a project for me:
I can now simply hit "Dotnet Run" like any other Dotnet Project and the stubbed out code runs like any other web application:
Notice that the application is running on port 5001 using HTTPs. I just hit https://localhost:5001 and then hit "Fetch Data" on the left to see an example of how data is fetched using Blazor:
Awesome. We now have an example with Blazor Server running. Let's take a quick look at the code to see what's going on. The first thing to look at is the startup file. There are a couple of things happening here:
Just like we do a "UseMvc" in a typical dotnet application, here we are the Server Side Blazor service to the service pipeline. We use the new Endpoint routing that comes with .NET Core 3.0 to Map a Signal-R hub Blazor uses internally. The Fallback route of "/_Host" is theoretically supposed to be hit when no routes match. This means you can use other controllers and pages without conflicting with Blazor routes, but when none of the other routes match, the _Host route acts as a starting point for the application.
The above _Host view has two aspects. After it lays out the head and body tags, it has a section that hosts the entire app and another section to display errors. The app section itself manifests itself as a view (app.razor) of what happens when a route is found and not found:
When routes like "/FetchData" are found the corresponding views are rendered the respective View Razor files are invoked:
Notice the HTML is similar to regular HTML and Rather other than the fact that it uses a local C# variable called forecasts which is declared in the @code block. The @block is where you write your c# code. If you don't prefer mixing HTML with C# you can actually extract this code out in a separate file which makes it very similar to the code behind model that we used with Web Forms in ASP.NET. The Forecast Service Class in the code above is just another C# class that runs on the server, which can then invoke Rest APIs and do things. In the stub is just returns hard coded data.
What's important to note here is that the C# code that write here is running on the server which means having an offline client is not possible. Also under the hood the server needs to keep a connection open using Signal-R with every connected client. Where I see this being used is small and quick prototypes or places where there is the going to be heavy use of Signal-R anyways and connection are going to be open with the server anyways all the time. A classic example is a real-time price ticker! If you need a more disconnected SPA experience you are better of moving to the client side model of Blazor.
Blazor Web Assemblies Example:
Even though this is in preview till May 2020, the tooling for building Blazor Web Assembly pages is also really awesome with .NET Core. I had to get .NET Core 3.1 (preview) for this to work though. Once I have the right version of the framework I create a new project using:
dotnet new blazorwasm -o clientexample
This stubs out a simple Web Assembly based project for me:
I built it and run it just like any other .NET project:
And we get:
I get the exact same output as the server example we did before but the underlying tech and design that's powering this example is completely different though. Let's take a look at the code to see what's different:
This project kicks off with a regular main method that basically utilizes the Blazor Web Assembly Host Builder to host your application. The App.razor and other aspects of your app might look similar to the server example that we tried out but what's strikingly different is the call to fetch the data:
Notice above that we are using the HttpClient library of C# directly on the client side and then passing the URL of a Json file to it. This could also be a URL of a service that returns Json. There is no backend server side in this app as far as fetching data is concerned and the client is doing most of the heavy lifting.
This design is pretty similar to any angular or client side application where the .NET pieces are just being used to start and host the application. All C# code that you put in your views is directly running on your client and using Http Client libraries to hit micro services or web-api's that run on the server.
The maturity of the tooling both on client side and the server side as far as Blazor is concerned has blown me away. All the complexity behind Web Assemblies and Signal-R are encapsulated rather elegantly by the tooling. Having said that, will I use Blazor in a production level application yet? I'm not sure.
The sever implementation of Blazor seems creepily similar to the code behind model of ASP.NET where the server has to do bulk of the processing. Unless it's a prototype or something really simple I'm building I'm not sure if I am ready to go that route.
If you are a web developer, Web Assemblies are a big paradigm change and Razor is Microsoft's bet on it, which is what really makes it worth spending some time on it and seeing if it fits your problem statement.