C# (and .NET) in WebAssembly

The .NET (aka dotnet) ecosystem supports a variety of languages, including C# (C sharp) and ASP.NET. Programs that target .NET are compiled to an intermediary bytecode format that runs in the .NET Common Language Runtime (CLR). This makes .NET an example of a virtual machine language.

An overview blog post of the state of Wasm, dotnet, and the cloud was published December 2023.

The roadmap to full Wasm and Component Model support in dotnet is tracked in this GitHub Issue, which is attached to dotnet’s 9.0.0 milestone, to be released in November 2024.

Available Implementations

The .NET ecosystem has long had support for browser-side WebAssembly via the Blazor toolkit.

An experiment for WASI support in dotnet .NET WASI SDK exists, on top of which Fermyon created an Spin SDK for dotnet. These can be used to try out Spin and C# using dotnet 8.

Pros and Cons

Things we like:

  • Blazor is enjoying a surge of popularity in the browser
  • The .NET WASI Runtime looks excellent
  • Microsoft is investing in Wasi support in dotnet, and have a roadmap for dotnet 9.0.0

The roadmap for dotnet to get NativeAOT support, and have the standard library support WASI 0.2.0 looks good, with an experimental release to be shipped with dotnet 9.0.0 in November 2024.

Example

There are two examples we want to show for dotnet:

  1. An example building a Spin component using dotnet and C#
  2. An example building a WebAssembly WASI module

Spin Dotnet Application

Our first example uses the Spin SDK for dotnet. This is an experimental preview and requires the prerequisites described in the repo.

All of our examples follow a documented pattern using common tools.

After having install the required tooling, install the templates for a Spin HTTP component using C#:

$ spin templates install --git https://github.com/fermyon/spin-dotnet-sdk
Copying remote template source
Installing template http-csharp...
Installed 1 template(s)

+------------------------------------------------------------+
| Name          Description                                  |
+============================================================+
| http-csharp   HTTP request handler using C# (EXPERIMENTAL) |
+------------------------------------------------------------+

Next, let’s create a new Spin application using that template:

$ spin new -t http-csharp
Enter a name for your new application: dotnet-spin
Project description: A Spin application written in C#
HTTP path: /...

Enter the newly created directory. If you used dotnet-spin as the name, that wil be the name of the directory.

$ cd dotnet-spin/

And you can now use the spin build --up command to build the WebAssembly, and run (up) the Spin application:

$ spin build --up
Building component dotnet-spin with `dotnet build -c Release`
MSBuild version 17.8.3+195e7f5a3 for .NET
  Determining projects to restore...
  Restored /Users/me/code/temp/dotnet-spin/Project.csproj (in 258 ms).
  Project -> /Users/me/code/temp/dotnet-spin/bin/Release/net8.0/DotnetSpin.dll
  1/26 Bundling DotnetSpin.dll...
  2/26 Bundling System.Collections.Concurrent.dll...
  3/26 Bundling Fermyon.Spin.Sdk.dll...
  ...
  26/26 Bundling System.Text.Encoding.Extensions.dll...
  DotnetSpin -> /Users/me/code/temp/dotnet-spin/bin/Release/net8.0/DotnetSpin.wasm
  Running wizer to preinitialize bin/Release/net8.0/DotnetSpin.wasm...

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:11.51
Finished building all Spin components
Logging component stdio to ".spin/logs/"

Serving http://127.0.0.1:3000
Available Routes:
  dotnet-spin: http://127.0.0.1:3000 (wildcard)

Open a new prompt in your terminal, and send a request to the Spin application:

$ curl localhost:3000
Hello from .NET

Congratulations, you’ve built a WebAssembly module using dotnet and Spin!

WebAssembly WASI Module

This second example uses the dotnet console project template to build a WebAssembly WASI module, which can be run using a WebAssembly runtime like Wasmtime.

For this example, the prerequisites are:

Let’s go ahead and create a new dotnet console project:

$ dotnet new console --name dotnet-wasi
The template "Console App" was created successfully.

Processing post-creation actions...
Restoring /Users/me/code/temp/dotnet-wasi/dotnet-wasi.csproj:
  Determining projects to restore...
  Restored /Users/me/code/temp/dotnet-wasi/dotnet-wasi.csproj (in 84 ms).
Restore succeeded.

Next, we’ll change to the directory created, and add Wasi.Sdk to the project.

$ cd dotnet-wasi

$ dotnet add package Wasi.Sdk
Determining projects to restore...
...
log  : Restored /Users/me/code/temp/dotnet-wasi/dotnet-wasi.csproj (in 95 ms).

Adding the Wasi.Sdk package ensures that we’ll be building a WebAssembly, so let’s go ahead and do so:

$ dotnet build
MSBuild version 17.8.3+195e7f5a3 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
  dotnet-wasi -> /Users/me/code/temp/dotnet-wasi/bin/Debug/net8.0/dotnet-wasi.dll
  1/10 Bundling dotnet-wasi.dll...
  2/10 Bundling System.Memory.dll...
  ...
  10/10 Bundling System.Private.Uri.dll...
  dotnet-wasi -> /Users/me/code/temp/dotnet-wasi/bin/Debug/net8.0/dotnet-wasi.wasm

Build succeeded.
    0 Warning(s)
    0 Error(s)

And let’s use Wasmtime to run the WebAssembly:

$ wasmtime run bin/Debug/net8.0/dotnet-wasi.wasm
Hello, World!

Learn More

Here are some great resources:

  • Blazor
  • Steven Sanderson from Microsoft has release a few videos with some great experiments with dotnet and Wasm. Check out his YouTube channel.