loading...
Netguru

Building WASM applications with C#

divoolej profile image Wojciech Olejnik Updated on ・4 min read

This post is a short summary of our recent experiment with WASM and Blazor (.NET)

Here at Netguru we have high hopes for WebAssembly. It's definitely a disruptive technology with a huge potential to revolutionize modern web development. So far though, we've struggled to come up with a good use case for it or to find a way to incorporate it in our day-to-day projects. We've experimented with Machine Learning and image processing, but the results were not very satisfying. Our technology of choice was always Rust, which has out of the box support for WASM and a great ecosystem of tools. But Rust also had some drawbacks, including it's infamous steep learning curve. When it comes to writing whole web apps in Rust, the only reasonable option seems to be Yew (https://github.com/yewstack/yew) and even though it looks very promising, it's still a work in progress.

Some time ago I stumbled across Blazor, a part of Microsoft's ASP.NET framework which enables writing client side web applications in C#. There are two flavours of Blazor - one works by executing C# code on the server and passing the data back using a technology named SignalR (basically WebSockets). The second flavour of Blazor works by compiling C# into WebAssembly. I was intrigued that Microsoft adopted this technology so quickly into their core framework, so we decided to perform some research and maybe write a simple application with it.

As of today, the WASM version of Blazor is still in Preview, but the documentation and support are very solid. Getting started is as easy as installing the preview version of Visual Studio (or the dotnet core CLI), downloading the blazorwasm project template, and then creating a project. This comes down to two commands:

# Download the blazorwasm project template
$> dotnet new -i Microsoft.AspNetCore.Blazor.Templates::3.1.0-preview3.19555.2
# Build a new project from the template
$> dotnet new blazorwasm --hosted

You can skip the --hosted part if you want to make just a client-side application. I also wanted to try building a simple API in ASP.NET and this option initializes a simple project for that.

The blazorwasm template contains three examples: a counter, a todo list, and a weather forecast API example. They're minimal and easy to understand, one can also get rid of them quickly (which I did because I like to build my stuff from scratch). For our research example, I've decided to implement a simple dashboard with fake financial data. Stock values are fetched every second from an API where they are adjusted randomly, so that we get some dynamic content in an otherwise simple application. The code that we actually write in a Blazor app can be divided into three areas: Razor templates, component logic and plain C# classes. Razor templates are very similar to other template solutions like JSX or ERB. The difference is of course in the syntax and the fact that we can embed C# code in them. Components in Blazor are again very similar to React or Vue - they are stateful and have specific lifecycle methods defined on them that we can use to manage their state. In my example I had to use OnInitializedAsync (an equivalent of React's componentDidMount), where I set up recurring data fetching. The template and the component logic reside in one file that looks like this:

@page "/"
@using Stocks.Shared
@inject StocksViewModel viewModel

<div class="dashboard">
  // We can use C# inside html if we preceed it with the "@" symbol.
  @if (viewModel.Stocks == null) {
    <p><em>Loading...</em></p>
  }
  else {
    // Rest of the page...
  }
</div>

@code {
  // Component logic goes here
  protected override async Task OnInitializedAsync() {
    await viewModel.FetchStocks();
    System.Timers.Timer t = new System.Timers.Timer();
    t.Elapsed += async (s, e) => {
      await viewModel.FetchStocks();
      await InvokeAsync(StateHasChanged); // Inform the component that it should re-render;
    }
    t.Interval = 1000;
    t.Start();
  }
}

Last but not least, we write pure C# classes to separate the logic from our views and components. An amazing advantage of this approach is the fact that we can actually share the same code between the client and server applications. In my case I had to write a ViewModel class for my Stock model to decorate some of the database fields.

The developer experience overall is pretty good, even on a Mac. Visual Studio is not required (any .NET core app can be run from a terminal), but it certainly brings a lot to the table with IntelliSense and an integrated debugger. That said, the development felt quite slow at times. Error messages in the browser were often extremely unhelpful due to the obfuscated nature of WebAssembly. There's also no hot reload on C# code changes (at least to my knowledge), which meant I had to stop and restart the application every couple of minutes.
When it comes to performance and the overall result of this experiment, I'm quite happy. The app runs smoothly and the only performance bottleneck is the initial load time - since the browser needs to download the WASM equivalent of the entire .NET framework. But these files would be served from browser cache in a normal scenario so I'm actually not worried about that.

I'm certainly intrigued by this new way to develop web applications. With Blazor coming out of preview and WebAssembly constantly evolving, I'm starting to think that it might become one of the hot technologies of the next few years.

Discussion

pic
Editor guide
Collapse
arctekdev profile image
Arctek 🧊

There's also no hot reload on C# code changes (at least to my knowledge)

Dotnet Watch.

Collapse
divoolej profile image
Wojciech Olejnik Author

Thanks! I wonder why this is not the default in Visual Studio

Collapse
arctekdev profile image
Arctek 🧊

Welcome!

Microsoft is moving towards making all their stuff more minimal. It's better to have the user decide for themselves if they need dotnet watch or not.

Collapse
metalmikester profile image
Michel Renaud

Good article, thanks!

I toyed with WASM a few weeks ago and, strangely, Intellisense didn't work (VS 2019 Community Edition, latest update installed). That was seriously annoying. I was used to no Intellisense in the '80s (that wasn't even a thing), but back then we were used to having entire languages in our heads. Not possible these days.

Maybe I'll give it another try, though I've pretty much decided to go another route for the front end at the moment.