Blazor – You Want To Run .NET Where?!

Jamie Taylor1 comment
Blazor Header ImageSource: https://unsplash.com/photos/F7Sive0fwIg Copyright 2017: Ahmad Dirini (@ahmadirini)

Today’s header image was created by Ahmad Dirini at Unsplash

Two Notes From The Future

Note One:

This article was written shortly after Blazor version 0.1.0 was released. Things have changed since then

in fact, I’m adding this update on April 18, 2018, hours after 0.2.0 has been released

As such, some of the code listings and links in this article only work with version 0.1.0.

Note Two:

I’ve taken my PokeBlazor app (which I reference a bunch of times in this post), and uploaded it to Azure. So if you want to see what the finished code looks like, then click here


A Quick Warning

I just wanted to say: this post is going to be a long one.


Unless you’ve been living under a rock for the past few weeks, you’ll have seen the announcement regarding Blazor being in released as a public review.

here’s a link to that announcement – which was made on March 22, 2018

It was also the subject of this week’s ASP.NET Community Standup, here’s a YouTube embed:

I’ve actually written about Blazor twice already

and hosted a mini lab/experiment session

on the blog for the company that I work for:

That’ll be the only time I bring up my employer in this post, I just wanted to point out that I’ve been playing with Blazor a fair bit already

What’s It All About

Cast your mind back to August of 2017, when I shared the following image (taken from one of the many .NET Core 2.0 announcement posts:

Any Developer, Any App, Any Platform
Just soak that in for a moment

This image was all about showing that you could run .NET on any platform, thanks to the wonderful work of Immo Landwerth and his team

they work on the .NET Standard

But there was one place that you still couldn’t run Blazor: in the web browser.

You may be asking yourself, “why?” I’ll (jokingly) answer with a quote from Ted Kennedy:

Some men see things as they are, and ask why. I dream of things that never were, and ask why not.

In a world where we have JavaScript on the server, what would be the harm of running a (primarily) desktop framework in the browser?

nothing, I tell you. Nothing

After all, running .NET in the browser is not a new idea. In fact, Blazor is actually based on an older, open source, experiment called “DotNetAnywhere“. This is something that Steve Sanderson (the “inventor” of Blazor) was very quick to point out in his NDC Oslo 2017 talk on the matter:

How Does It Work?

The short answer is: an ASP.NET Core middleware

you remember what they are, right?

is added to your project which takes the start point of another .NET app; this second app is then run by WebAssembly in the browser.

The long answer is:

Blazor is provided as an ASP.NET Core middleware, the middleware is added to an ASP.NET Core application and does the following at boot:

  • loads blazor.js which
  • loads mono.js which provides two way communication between your browser and WebAssembly
  • loads webasm.js which loads WebAssembly and (on the instruction of mono.js) sets up a .NET environment
  • loads your compiled .NET application
  • runs the .NET application

this is the paraphrased version

The first thing you might be thinking is, “but how does the UI work?” That’s where Blazor Components come in. They look a lot like Razor Pages, but are rendered differently (due to the Render Tree Builder).

Requirements

Before we see Blazor in action, it’s worth pointing out that there are a few provisos to working with Blazor right now.

The information in this section was correct at the time of this blog post going live

In order to get access to the Blazor templates (for the dotnet new CLI) you’ll need to run the following command in the terminal:

dotnet new -i Microsoft.AspNetCore.Blazor.Templates

You can then confirm that they were installed correctly by running:

dotnet new --list

which should give you something like:

Templates Short Name Language Tags
-----------------------------------------------------------------------------------------------------------
Console Application console [C#], F#, VB Common/Console
Class library classlib [C#], F#, VB Common/Library
Unit Test Project mstest [C#], F#, VB Test/MSTest
xUnit Test Project xunit [C#], F#, VB Test/xUnit
Blazor (hosted in ASP.NET server) blazorhosted [C#] Web/Blazor/Hosted
Blazor (standalone) blazor [C#] Web/Blazor/Standalone
ASP.NET Core Empty web [C#], F# Web/Empty
ASP.NET Core Web App (Model-View-Controller) mvc [C#], F# Web/MVC
ASP.NET Core Web App razor [C#] Web/MVC/Razor Pages
ASP.NET Core with Angular angular [C#] Web/MVC/SPA
ASP.NET Core with React.js react [C#] Web/MVC/SPA
ASP.NET Core with React.js and Redux reactredux [C#] Web/MVC/SPA
ASP.NET Core Web API webapi [C#], F# Web/WebAPI
global.json file globaljson Config
NuGet Config nugetconfig Config
Web Config webconfig Config
Solution File sln Solution
Razor Page page Web/ASP.NET
MVC ViewImports viewimports Web/ASP.NET
MVC ViewStart viewstart Web/ASP.NET

your list of templates returned will differ, based on the templates that you have installed

In order to compile Blazor applications, you will need to install the latest preview of the .NET Core SDK, this is because the current RTM versions of the SDK don’t support it. To do that, head over to this page and download the package for your operating system

at the time of writing, the latest preview was for 2.1.300-preview1

If you are using Visual Studio (on Windows), then you will need to download the latest preview of Visual Studio 15.7, which can be downloaded at this page. You’ll also need to install this Visual Studio extension in order to access the new templates in VS 15.7 Preview.

There is an ongoing discussion on the OmniSharp GitHub repo about implementing Blazor support, as such there is no support from those folks just yet

they do take pull requests, though 😉

If you’re using Visual Studio for Mac, then I would suggest using a different IDE and the terminal for now. But that’s only because there is no official word about Blazor support for it yet.

During this week’s ASP.NET Community Standup, Dan Roth mentioned that VS for Mac tooling and support is something that they are aiming for with the next release of the SDK.

Rider also has no native support for Blazor. Although I’ve found it much easier to use Rider to edit my Blazor projects

yes, projects. Plural

with Rider and run them with the terminal.

I’d recommend choosing whichever option you’re most comfortable with. As we’ll see, Blazor requires a bit of a shift in mind set.

Let’s Make a Blazor App

We’ll start from nothing and add create a brand new Blazor app. We’ll have it communicate with an external API, as that’s one of the things that Blazor is fantastic for. We’ll use the Pokemon API

you do know that I’m a big video games nerd, right

and we’ll throw in a little semantic ui, just to keep things interesting.

I’ll also be using the terminal (command prompt in Windows) to build and run the app, and Rider to edit the source code. Feel free to use whatever you’re most comfortable with.

I’ve already pushed this code (and a little more) to a public GitHub repo so if you want to read through it, you can do so here. We’re not going to cover all of the code in that repo, just the basics in order to get us going

I’ll talk through the code needed to search for and display the basic data of a Pokemon

A New Project

In the terminal issue the following command:

dotnet new blazorhosted --name pokeBlazor

This will create the exact structure that we need for the Blazor application (including a solution file). Open the solution file with your editor of choice

or open the directory with VS Code

and you should see a solution layout which looks a little like the following:

Blazor Solution Layout

I took this screenshot from within Rider, if you’re using a different IDE then it will look slightly different.

If you run the application, you’ll notice something very familiar – if you’ve used the SPA templates before, that is:

Blazor Default App
The default Blazor app should look very familiar, if you’ve done any work with the ASP.NET Core Angular template
Running From the Terminal

In order to run the application from the terminal, you’ll need to change into the server directory

more on the individual projects in a moment

and run from there. The commands you’ll need to use (assuming that you’re in the pokeBlazor directory) are:

cd pokeBlazor.Server
dotnet run

Break It Down

We have three projects in this Blazor application:

  • Client
  • Server
  • Shared

Starting from the bottom up…

Shared

This project is a .NET Standard 2.0 class library and is meant to be used to store any POCOs which you require for both the Server and Client projects

more on those in a moment

I haven’t tended to use the Server for anything other than serving my Client app in my Blazor apps, so I don’t tend to have a reference to it in the Server. The default template will have a reference to it – in order to use theWeatherForecastclass.

But what’s great about this pattern is that you’re using the same POCO on the server and in the client. You no longer have to keep two separate models (one in C# and one in TypeScript) in sync

fantastic

Client

This is the application which will be compiled and run in the browser. It (currently) makes calls to the server project’s API in order to get data for the “Fetch Data” page, and hydrates the response into an instance of the WeatherForecast class.

The compiled output of this class will be run on the Server. Any time that you make a change to the Client project, you’ll need to stop the entire solution and start it again.

this is a key point and throws a lot of people when they start working with Blazor

The Blazor team are looking to change this in the next few releases, by leveraging similar technology behind hot module swapping.

Server

The server will serve HTML and JS which will bootstrap a .NET environment in your browser. It will then serve your compiled Client application to the browser, and run it. Almost all of the magic required to get the application to start will be handled for you.

Request ‘Em All

We’re about to break the application by making the Client request information from the Pokemon API. This is an amazingly well documented RESTful API for all sorts of Pokemon data. If you want, you can take a few moments familiarising yourself with it before we begin.

If you’re using VS Code or Rider, you’ll already be seeing some syntax errors. You can ignore them, as long as you were able to run the application earlier

now would be a good time to double check that it still runs

Step One – Clean Up

There isn’t much in this application that we don’t need but let’s remove it anyway, otherwise it might get in the way of our very important work.

Firstly delete the WeatherForecast.cs class from the Shared project

and watch the syntax errors come in

Then remove the Controllers directory from the Server project and the following lines from it’s Startup.cs:

app.UseMvc(routes =>
{
routes.MapRoute(name: "default", template: "{controller}/{action}/{id?}");
});

We only need the Server to serve our Client application (i.e. we no longer need the API that the server project supplied).

Also remove the references to pokeBlazor.Shared.csproj in the pokeBlazor.Server.csproj file by removing the following line:

<ProjectReference Include="..\pokeBlazor.Shared\pokeBlazor.Shared.csproj" />

In the Client application delete the Pages and Shared directories, rename App.cshtml to be Main.cshtml and clear it’s contents.

Alter Program.cs file (in the Client project) to read:

using Microsoft.AspNetCore.Blazor.Browser.Rendering;
using Microsoft.AspNetCore.Blazor.Browser.Services;
using Microsoft.Extensions.DependencyInjection;
using PokeBlazor.Client.Services;
namespace pokeBlazor.Client
{
public class Program
{
static void Main(string[] args)
{
var serviceProvider = new BrowserServiceProvider(configure =>
{
configure.AddSingleton<PokeState>();
});
new BrowserRenderer(serviceProvider).AddComponent<Main>("app");
}
}
}

Ensure that the _ViewImports.csthml file (in the Client project) reads:

@using System.Net.Http
@using Microsoft.AspNetCore.Blazor
@using Microsoft.AspNetCore.Blazor.Components
@using Microsoft.AspNetCore.Blazor.Layouts
@using Microsoft.AspNetCore.Blazor.Routing
@using pokeBlazor.Client
@using pokeBlazor.Client.Services

Remove the following (highlighted lines) in the index.html file (found in the wwwroot directory):

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>pokeBlazor</title>
<base href="/" />
<link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
<link href="css/site.css" rel="stylesheet" />
</head>
<body>
<app>Loading...</app>
<script src="css/bootstrap/bootstrap-native.min.js"></script>
<script type="blazor-boot"></script>
</body>
</html>

Finally delete the bootstrap and fonts directories from the wwwroot directory’s css subdirectory.

Your Blazor solution layout should look something like this:

Post Clean Up PokeBlazor

Step Two – Let’s Add A Component and a Service

Blazor Components, if you don’t know, use Tag Helpers to add components to a page. The tag helpers are auto expanded, and their rendered content are added to the page, during the Render Tree Builder’s work.

Tag Helpers look a little like HTML tags, but they aren’t valid HTML tags, as we’ll see in a moment.

Creating a Component

Create a directory in the Client project called Components, create a file called RandomPokemonSearch.cshtml, and paste the following code into it:

<div class="ui grid">
<div class="row">
<div class="ui two column centered grid">
<h1>PokeyMans Decks</h1>
</div>
</div>
<div class="row">
<div class="ui two column centered grid">
<p>Enter the ID of a PokeyMans in the input box and click on `Search` to find out about that PokeyMans</p>
</div>
</div>
<div class="row">
<div class="ui two column centered grid">
<input type="text" @bind(PokemonId) />
</div>
<div class="ui two column centered grid">
<button @onclick(() => OnGetPokemon(PokemonId)) type="button" class="ui secondary button">Search</button>
</div>
</div>
</div>
@functions
{
public Func<string, Task> OnGetPokemon { get; set; }
public string PokemonId { get; set; }
}

Most of this file reads like standard HTML, right up until the @functions section. This is where the Blazor Components stuff comes in.

Essentially the @functions section is a C# class. What we’ve done is inlined something like the following:

namespace pokeBlazor.Client
{
public class RandomPokemonSearch
{
public Func<string, Task> OnGetPokemon { get; set; }
public string PokemonId { get; set; }
public RandomPokemonSearch()
{
}
}
}

but we’ve also made the properties of this class available throughout the RandomPokemonSearch component. This is what the @bind directives are for (we’re binding the controls in the markup to the properties of the class).

Handling State with a Service

We need to add a service to the Client project. The service that we’ll create holds the state of our Client app when running in the browser

remember that http is stateless

Create a directory in the Client project called Services, add a file called PokeState to it, and add the following code to that file:

using System;
using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Blazor;
using pokeBlazor.Shared;
namespace pokeBlazor.Client.Services
{
public class PokeState
{
public Pokemon PokemonSearchResult { get; private set; }
public bool SearchInProgress { get; private set; }
// Lets components receive change notifications
public event Action OnChange;
// Receive 'http' instance from DI
private readonly HttpClient http;
public PokeState(HttpClient httpInstance)
{
http = httpInstance;
}
public async Task GetPokemon(string id)
{
SearchInProgress = true;
NotifyStateChanged();
PokemonSearchResult = await http.GetJsonAsync<Pokemon>($"https://pokeapi.co/api/v2/pokemon/{id}/");
SearchInProgress = false;
NotifyStateChanged();
}
private void NotifyStateChanged() => OnChange?.Invoke();
}
}

This is almost everything that we need to do for this step

we’ll go through what all of this code does in a moment

We just need to add the Pokemon class and use the RandomPokemonSearch component in the Main.cshtml file.

The Pokemon Class – Gotta Represent ‘Em All

The Pokemon class will be based on the data returned when you issue a request to the Pokemon API for a given critter. The information returned for Bulbasaur, for example, looks like this:

{
"name":"bulbasaur",
"weight":69,
"location_area_encounters":"\/api\/v2\/pokemon\/1\/encounters",
"height":7,
"is_default":true,
"base_experience":64
}

I’ve removed a lot of information here. For the full response, issue a request from your browser or click the link above this code block.

Create a file called Pokemon.cs in the Shared project and paste this code into it:

namespace pokeBlazor.Shared
{
public class Pokemon
{
public int id { get; set; }
public string name { get; set; }
public bool is_default { get; set; }
public int base_experience {get; set;}
public int weight {get; set;}
public int height {get; set;}
}
}
Caveat

We want the application to download to the browser as quickly as possible, so I’ve intentionally ignored the C# standard for public properties. I could have included Newtonsoft.Json so that I could have well named C# properties and have them mapped to the JSON data nicely, but Newtonsoft.Json is huge.

That’s not a negative against Newtonsoft.Json, as it does a lot of stuff. But I don’t need most of the stuff that it provides, so I’ll leave it out for now.

When you’re finished with this blog post, feel free to add NewtonSoft.Json and the relevant attributes to automatically map from the JSON to the POCO and see just how much longer the app takes to download

if you wish

Using Our Component

Over in the Main.cshtml file (in the Client project), add the following code:

@inject PokeState state
<div id="search-and-results-area">
<div id="search-area">
<RandomPokemonSearch OnGetPokemon=state.GetPokemon/>
</div>
</div>
@functions
{
protected override void OnInit()
{
state.OnChange += StateHasChanged;
}
}

But What Did I Just Do?

If you run the Blazor application

remember to stop and start it if it’s still running from eariler

you’ll see a pretty ugly screen (once the compiled binary and the linked namespace binaries have been served to your browser fully):

Unstyled Blazor App
Just don’t show a UX dev

Once the Client application had been downloaded, it was started via the Main method in the Client project’s Program.cs. This method does two things:

  • Add an instance of the PokeState class as a Singleton
  • Add the contents of the Main.csthml file to the body tag in the HTML which was served from the Server project

The Main.cshtml file was then put through the Render Tree which did the following actions:

  • Injected the PokeState singleton into the C# class
  • Created an OnInit method and set up a delegate to the OnChange event
  • Inlined the rendered content of the RandomPokemonSearch component
  • It also set the value of the OnGetPokemon method on the RandomPokemonSearch component to the GetPokemon method (from PokeState)

The RandomPokemonSearch.csthml file was then rendered by the Render Tree, which did the following:

  • The value of PokemonId was bound to the input text box
  • The onclick method for the button was bound to the OnGetPokemon method

actually parts of these two steps happen in the opposite order, but it’s easier to think about it this way

When the button is clicked, Blazor will call the GetPokemon method on the PokeState service. This service acts as our state provider, as such we set up an Action which acts as a sort of callback. This action will tell the BrowserRenderer class that it need to re-render the HTML being served on the browser.

When this happens, the Main.cshtml file (and all of the components used in it) are put back through the Blazor Render Tree and the HTML is swapped out in the browser.

Just. Like. Magic.

Can We Make The App Better?

Let’s add the semantic ui files.

In the Client project’s wwwroot directory, add two subdirectories:

  • CSS
  • JS

In the CSS directory, create a subdirectory called `semanticui`, and add the version of the semantic-ui.min.css file found in the GitHub repo for this project.

In the JS directory, create a subdirectory called `semanticui`, and add the version of the semantic-ui.min.js file found in the GitHub repo for this project.

Then edit your index.cshtml file (in the Client project’s wwwroot subdirectory) to look like this:

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>DwBlazor</title>
<base href="/" />
<link rel="stylesheet" type="text/css" href="css/semanticui/semantic.min.css">
<link rel="stylesheet" type="text/css" href="css/site.css">
<script
src="https://code.jquery.com/jquery-3.1.1.min.js"
integrity="sha256-hVVnYaiADRTO2PzUGmuLJr8BLUSjGIZsDYGmIJLv2b8="
crossorigin="anonymous"></script>
<script src="js/semanticui/semantic.min.js"></script>
<script type="blazor-boot"></script>
</head>
<body>
Please wait...
</body>
</html>

I’ve highlighted the important lines that you’ll need to copy in

Then stop and start the application and the UI should look a little better:

Semantic UI Version of PokeBlazor
I’m sure you’ll agree, that looks so much better

Just One More Thing

The last thing we’ll do is render the data which was parsed from the Pokemon API response.

It’s the Final Component

Create a new component called RandomPokemonResult and paste the following code into it:

@using pokeBlazor.Shared
@if (PokemonToDisplay != null)
{
<div class="ui grid">
<div class="row">
<div class="ui two column centered grid">
<h2>@PokemonToDisplay.name</h2>
</div>
</div>
<div class="row">
<div class="ui two column centered grid">
<div class="two wide column">
<label>Weight: @PokemonToDisplay.weight</label>
</div>
<div class="two wide column">
<label>Height: @PokemonToDisplay.height</label>
</div>
<div class="two wide column">
<label>Base Exp: @PokemonToDisplay.base_experience pt</label>
</div>
</div>
</div>
</div>
}
@functions
{
// Parameters
public Pokemon PokemonToDisplay { get; set; }
}

Then edit the Main.cshtml file to read like this:

@inject PokeState state
<div id="search-and-results-area">
<div id="search-area">
<RandomPokemonSearch OnGetPokemon=state.GetPokemon/>
<RandomPokemonResult PokemonToDisplay=state.PokemonSearchResult/>
</div>
</div>
@functions
{
protected override void OnInit()
{
state.OnChange += StateHasChanged;
}
}

Finally stop and start the application again, enter “1” in the text box and click the “Search” button and you should get something like the following:

PokeBlazor Bulbasaur Data

keep going with other non-negative IDs and see which Pokemon you find

Conclusion

Together we just created an incredibly useful Blazor application

ok, I’m probably inflating the app’s importance there. I mean, it’s never going to win any awards or change the world. But a man can dream, though.

We learnt about how Blazor does its thing, how to create a Blazor app from nothing, and how to use it to talk to an external API.

There’s a lot more to Blazor

which I might cover in a later blog post

and how amazing it is. But I’ll stop this post here, as it’s pretty long already.


Have you used Blazor for anything yet? If so, what did you build? If not, why not? It’s super easy to use.

Also, take a look at the GitHub repo that I built alongside this blog post. It’s more complete than the example that we built together. Plus, it has a few issues for the known bugs with the application

because there are a few

Jamie Taylor
A .NET developer specialising in ASP.NET MVC websites and services, with a background in WinForms and Games Development. When not programming using .NET, he is either learning about .NET Core (and usually building something cross platform with it), speaking Japanese to anyone who'll listen, learning about languages, writing for his non-dev blog, or writing for a blog about video games (which he runs with his brother)