Blazor is coming! In fact, it’s coming sooner than later. Check out ASP.NET’s blog post from April 18, 2019 announcing the official preview.
What is Blazor?
by Vivek Gunnala
Blazor is a new interactive .Net Web framework, which is part of the open-source .Net platform. Blazor uses C#, HTML, CSS and Razor components instead of JavaScript. It’s built on open web standards without the need for any plugins or code transpilation, and it works on all modern web browsers, hence called “.Net in the browser”, the C# code is directly run on the browser using WebAssembly. Both client-side code and server-side code is developed in C#, which allows you to reuse code and libraries between both sides, such as validations, models, etc.
Apps built in Blazor can use existing .Net libraries by leveraging .Net Standard, allowing the same code to be used across platforms. Since it is an experimental project, Blazor is evolving rapidly with over 60,000 contributors.
About WebAssembly
At a high-level, WebAssembly is explained on the official site as, “a binary instruction format a stack-based virtual machine. It is designed as a portable target for compilation of high-level languages, enabling deployment on the web for client and server applications.”
Should I Use Blazor For My Next Project?
by Ash Tewari
Blazor’s development status has been promoted from an “Experimental” project to a committed product. This is great news. Blazor is available now as an official preview. Let’s review the factors you should consider when making decisions about adopting Client-Side Blazor for your next production project.
Mono.wasm (The .NET runtime compiled into WebAssembly executing your .NET assemblies in the browser) does not interact with the DOM directly. It goes through JS Interop, which is expensive. The areas where .NET Code will get the most net benefit is in the Model and Business Logic, not the DOM manipulation. If your application is very chatty with the DOM, you might need to carefully assess whether you are getting the expected performance boost from WebAssembly execution of your .NET assemblies. [https://webassemblycode.com/webassembly-cant-access-dom/]
Currently, only the mono runtime is compiled to WebAssembly. Your .NET code is executed as-is. This means that your .NET code is essentially going through two interpreters and it has a noticeable performance impact. There is work being done to compile .NET assemblies to wasm. That and other related improvements in linking and compiling) is expected to improve the performance. The fact that Microsoft has decided to commit Blazor as a product indicates that there is confidence that these performance improvements are likely to become a reality.
[https://www.mono-project.com/news/2018/01/16/mono-static-webassembly-compilation/, https://github.com/WebAssembly/reference-types/blob/master/proposals/reference-types/Overview.md]
In the client-side hosting model, your code is still running in the browser sandbox. So, you don’t have any access to FileSystem and other OS libraries. This limitation applies to javascript as well. In fact, WebAssembly is executed by the Javascript runtime. Yes, the same runtime which is executing the javascript in your web application.
[https://medium.com/coinmonks/webassembly-whats-the-big-deal-662396ff1cd6]
Well, if WebAssembly is executed by the same Javascript runtime, then where are the performance gains everyone is touting about coming from? The answer is that the performance gains come from skipping the parsing steps and/or optimizing compilation steps. The WebAssembly is decoded and JITed instead of parsed and compiled before the JIT step. However, there is work still ongoing to make .NET IL interpretation reach the performance levels required to fulfill the promises
[https://hacks.mozilla.org/2017/02/what-makes-webassembly-fast/]
Remember that your Blazor code executes in the UI thread of the browser, which can create a bottleneck if your application is CPU bound. Ironically, the CPU/computationally intensive applications are also one of the most compelling use-cases for Blazor. You may need to look into running Blazor components in the Web Worker. We will cover this in a separate blog post dedicated to this technique.
Server-Side Blazor
by Sean McGettrick
Server-side Blazor, previously referred to as Razor Components, allows developers the same freedom to create UI components using C# instead of Javascript that client-side Blazor does. The primary difference being that the code is hosted on the server instead of the browser. Blazor components and application logic written to run client-side can also be used server-side.
Razor Components support all the functionality a front-end developer would expect in a modern library including:
- Parameterization
- Event handling
- 2-way data binding
- Routing
- Dependency injection
- Layouts
- Templating
- CSS cascading
Razor Components can be nested and reused, similar to React.
Differences from Client-Side
With server-side Blazor, all components are hosted and served from an ASP.NET Core server instead of being run in the browser via WASM. Communication between client and server are handled via SignalR.
Further differences between client and server-side Blazor will be outlined in the next two sections.
Advantages
Server-side Blazor offers a number of advantages over its client-side counterpart. These include:
- No WASM dependencies. Older desktop browsers and some current mobile browsers lack support for WASM. Since server-side Blazor only requires the browser to be able to support Javascript it can run on more platforms.
- Building on the last point, since the components and application logic sit server-side, the application is not restricted to the capabilities of the browser.
- Developing the application on an entirely server-based platform allows you access to more mature .NET runtime and tooling support.
- Razor components have access to any .NET Core compatible API.
- Application load times in the browser are faster due to a smaller footprint. Only the SignalR Javascript code required to run the application is downloaded to the client.
Disadvantages
There are, however, some disadvantages to using server-side Blazor:
- There is higher application latency due to user interactions requiring a network round-trip between the browser and the server.
- Since the application is entirely hosted on the server, there is no offline support. If the server goes down, the application will not function which breaks one of the core tenets of building a Progressive Web Application (“Connectivity independent: Service workers allow work offline, or on low-quality networks”).
- With the server being responsible for maintaining client state and connections, this can create difficulty in scaling the application since the server is doing all the work.
- The application must be hosted on an ASP.NET Core server.
Server-Side Blazor Code Re-Use, Razor Pages to Blazor using an MVVM approach
by Adam Vincent
What is MVVM?
In a nutshell, MVVM is a design pattern derived from the Model-View-Presenter (MVP) pattern. The Model-View-Controller (MVC) pattern is also derived from MVP, but where MVC is suited to sit on top of a stateless HTTP protocol, MVVM is suited for user interface (UI) platforms with state and two-way data binding. MVVM is commonly implemented in Desktop (WPF / UWP), Web (Silverlight), and Mobile (Xamarin.Forms) applications. Like the other frameworks, Blazor acts much like a Single Page Application (SPA) that has two-way data binding and can benefit from the MVVM pattern. So whether you have existing MVVM code in the form of a WPF or mobile application, or are starting green with new code, you can leverage MVVM to re-use your existing code in Blazor, or share your code with other platforms.
You can find more information on MVVM on Wikipedia.
Example Presentation Layer
BindableBase
At the heart of MVVM is the INotifyPropertyChanged
interface which notifies clients that a property has changed. It is through this interface that converts a user interaction into your code being called. Usually, all ViewModels, and some Models will implement INotifyPropertyChanged
therefore, it is common to either use a library (Prism, MVVM Light, Caliburn) or to create your own base class. What follows is a minimal implementation of INotifyPropertyChanged
.
public abstract class BindableBase : INotifyPropertyChanged { protected bool SetField<T>(ref T field, T value, [CallerMemberName] string propertyName = null) { if (EqualityComparer<T>.Default.Equals(field, value)) return false; field = value; OnPropertyChanged(propertyName); return true; } public event PropertyChangedEventHandler PropertyChanged; protected void OnPropertyChanged([CallerMemberName] string propertyName = null) { PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); } }
In this simplified model class, which derives from BindableBase
, we have a CustomerModel
with a single property FirstName
. In this context we would probably have a customer filling out an input within a form on a website where they must fill in their first name. This input would be bound to an instance of CustomerModel
on the ViewModel. While the customer is filling out the form, since we are in a two-way data binding scenario, each time the customer enters or removes a character from the form’s input box, SetField()
is called and will cause the PropertyChanged
event to fire.
public class NewCustomerModel : BindableBase { private string firstName; public string FirstName { get => firstName; set { SetField(ref firstName, value); } } }
Learn More: If you need to know more about INotifyPropertyChanged
the Microsoft Docs cover this topic very well.
Model
With INotifyPropertyChanged
out of the way, here is the entire presentation model.
public class NewCustomerModel : BindableBase { [Display(Name = "Customer Number")] public string CustomerNumber { get; set; } [Display(Name = "Full Name")] public string FullName => $"{FirstName} {LastName}"; private string firstName; [Required] [Display(Name = "First Name")] public string FirstName { get => firstName; set { SetField(ref firstName, value); OnPropertyChanged(nameof(FullName)); } } private string lastName; [Required] [Display(Name = "Last Name")] public string LastName { get => lastName; set { SetField(ref lastName, value); OnPropertyChanged(nameof(FullName)); } } [Display(Name = "Address")] public string Address => $"{Street}, {City}, {State} {PostalCode}"; private string street; [Required] [Display(Name = "Street Address")] public string Street { get => street; set { SetField(ref street, value); OnPropertyChanged(nameof(Address)); } } private string city; [Required] [Display(Name = "City")] public string City { get => city; set { SetField(ref city, value); OnPropertyChanged(nameof(Address)); } } private string state; [Required] [Display(Name = "State")] public string State { get => state; set { SetField(ref state, value); OnPropertyChanged(nameof(Address)); } } private string postalCode; [Required] [Display(Name = "Zip Code")] public string PostalCode { get => postalCode; set { SetField(ref postalCode, value); OnPropertyChanged(nameof(Address)); } } }
There are a few things to point out in this presentation model. First, please note the use of the Data Annotation attributes such as [Required]
. You can decorate your properties to provide rich form validation feedback to your users. When the customer is filling out a form and misses a required field it will not pass the model validation. This will prevent the form from being submitted as well as provide an error message if one is configured. We will cover this more in the View section
The next thing I wanted to point out is I’ve covered SetField()
in the INotifyPropertyChanged
section, but there is an additional bit of complexity.
[Display(Name = "Full Name")]
public string FullName => $"{FirstName} {LastName}";
Note that the FullName
property is a { get; }
-only concatenation of the customer’s first and last name. Since we are forcing the customer to fill out first and last name in a separate form field, changing either the first or last name causes the FullName
to change. We want the ViewModel to be informed of any changes to FullName
.
private string firstName; [Required] [Display(Name = "First Name")] public string FirstName { get => firstName; set { SetField(ref firstName, value); OnPropertyChanged(nameof(FullName)); } }
After the SetField()
is invoked in the base class, there is an additional call to OnPropertyChanged()
, which lets the ViewModel know that in addition to FirstName changing, FullName
has also changed.
Example ViewModel Interface
The example ViewModel below will expand on the model above. We’ll be using a simplified user story of “Creating a New Customer.”
Blazor supports .NET Core’s dependency injection out of the box, which makes injecting a ViewModel very simple. In the following ViewModel interface, we’ll need our concrete class to have an instance of NewCustomer
as well as a method which knows how to create a new customer.
public interface ICustomerCreateViewModel { NewCustomerModel NewCustomer { get; set; } void Create(); }
And the concrete implementation of ICustomerCreateViewModel
:
public class CustomerCreateViewModel : ICustomerCreateViewModel { private readonly ICustomerService _customerService; public CustomerCreateViewModel(ICustomerService customerService) { _customerService = customerService; } public NewCustomerModel NewCustomer { get; set; } = new NewCustomerModel(); public void Create() { //map presentation model to the data layer entity var customer = new NewCustomer() { CustomerNumber = Guid.NewGuid().ToString().Split('-')[0], FullName = $"{newCustomer.FirstName} {NewCustomer.LastName}", Address = $"{newCustomer.Address}, {NewCustomer.City}, {newCustomer.State} {NewCustomer.PostalCode}" }; //create _customerService.AddNewCustomer(customer); } }
ViewModel Deep-Dive
In the constructor, we’re getting an instance of our ICustomerService
which knows how to create new customers when provided the data layer entity called NewCustomer
.
I need to point out that NewCustomer
and NewCustomerModel
serve two different purposes. NewCustomer
, a simple class object, is the data entity used to persist the item. NewCustomer
), but on the form backed by the NewCustomerModel
presentation model, we want the customer to fill out multiple properties, ‘First Name’ and ‘Last Name’.
In the ViewModel, the Create()
method shows how a NewCustomerModel
is mapped to a NewCustomer
. There are some tools that are very good at doing this type of mapping (like AutoMapper), but for this example the amount of code to map between the types is trivial. For reference, what follows is the data entity.
public class NewCustomer { public string CustomerNumber { get; set; } public string FullName { get; set; } public string Address { get; set; } }
Opinionated Note: Presentation models and data entities should be separated into their respective layers. It is possible to create a single CustomerModel
and use it for both presentation and data layers to reduce code duplication, but I highly discourage this practice.
View
The last and final piece to the MVVM pattern is the View. The View in the context of Blazor is either a Page
or Component
, which is either a .razor file, or a .cshtml file and contains Razor code. Razor code is a mix of C# and HTML markup. In the context of this article, our view will be a customer form that can be filled out. There is also a button that calls the ViewModel’s Create()
method when the form has been filled out properly according to the validation rules.
@page "/customer/create" @using HappyStorage.Common.Ui.Customers @using HappyStorage.BlazorWeb.Components @inject Microsoft.AspNetCore.Components.IUriHelper UriHelper @inject HappyStorage.Common.Ui.Customers.ICustomerCreateViewModel viewModel <h1>Create Customer</h1> <EditForm Model="@viewModel.NewCustomer" OnValidSubmit="@HandleValidSubmit"> <DataAnnotationsValidator /> <ValidationSummary /> <div class="form-group"> <h3>Name</h3> <LabelComponent labelFor="@(() => viewModel.NewCustomer.FirstName)" /> <InputText class="form-control" bind-Value="@viewModel.NewCustomer.FirstName" /> <LabelComponent labelFor="(() => viewModel.NewCustomer.LastName)" /> <InputText class="form-control" bind-Value="@viewModel.NewCustomer.LastName" /> </div> <div class="form-group"> <h3>Address</h3> <LabelComponent labelFor="@(() => viewModel.NewCustomer.Street)" /> <InputText class="form-control" bind-Value="@viewModel.NewCustomer.Street" /> <LabelComponent labelFor="@(() => viewModel.NewCustomer.City)" /> <InputText class="form-control" bind-Value="@viewModel.NewCustomer.City" /> <LabelComponent labelFor="@(() => viewModel.NewCustomer.State)" /> <InputText class="form-control" bind-Value="@viewModel.NewCustomer.State" /> <LabelComponent labelFor="@(() => viewModel.NewCustomer.PostalCode)" /> <InputText class="form-control" bind-Value="@viewModel.NewCustomer.PostalCode" /> </div> <br /> <button class="btn btn-primary" type="submit">Submit</button> <button class="btn" type="button" onclick="@ReturnToList">Cancel</button> </EditForm>
The first thing to note is at the top of the code. This is how we use dependency injection to get an instance of our ViewModel.
@inject HappyStorage.Common.Ui.Customers.ICustomerCreateViewModel viewModel
Easy! Next, we need to create the form. The
needs an instance of a model to bind to, our NewCustomer
ViewModel, and a method to call when the user submits a valid form.
<EditForm Model="@viewModel.NewCustomer" OnValidSubmit="@HandleValidSubmit"> ... </EditForm>
Next, we bind each property to their respective input fields. Blazor has some built-in
helpers which help you accomplish the binding. They are still under development and you may find some features are lacking at the time of writing. Please refer to the docs in the note below for more up-to-date info.
Note: The
is something I’ve created as a replacement for the asp-for
tag-helper that retrieves the DisplayAttribute
from the presentation model classes. That code is available in the GitHub repository listed at the top.
<LabelComponent labelFor="@(() => viewModel.NewCustomer.FirstName)" /> <InputText class="form-control" bind-Value="@viewModel.NewCustomer.FirstName" /> <LabelComponent labelFor="(() => viewModel.NewCustomer.LastName)" /> <InputText class="form-control" bind-Value="@viewModel.NewCustomer.LastName" />
The magic here is bind-Value
which binds our
text box to the value of the ViewModel’s instance of the NewCustomerModel
presentation model.
Note: You can view full documentation on Blazor Forms and Validation here.
Last but not least, we’ll need some code to call our ViewModel’s Create()
method when the form is submitted and valid. We’ll also need the onclick=ReturnToList
I’ve defined for the Cancel button.
@functions { private void HandleValidSubmit() { viewModel.Create(); ReturnToList(); } private void ReturnToList() { UriHelper.NavigateTo("/customers"); } }
Conclusion
That’s it! In summary, I’ve covered what MVVM is, how Blazor can benefit from it, as well as an in-depth look at a simple example of how we can create a form with validation and rich feedback to the user. It is also important to reiterate that this example works not only in Blazor but can also be used in Windows Presentation Foundation (WPF) desktop applications as well as on other platforms. Please check out the GitHub repository as I continue to develop and expand on this concept.
Developer Gotchas
by Morgan Baker
Working with a new framework like Blazor always has its learning experiences. The goal of this section is to help alleviate headaches by providing common problems and solutions we encountered with Blazor.
- My localization isn’t working!
For this problem, check your route parameters. Depending on the type of parameter, the invariant culture is used by the route by default, allowing for no localization for URLs. This can be solved by allowing the parameter to be passed in as any type, and then validating the type in C# code before using it. - I can’t debug my C# code!
Server-side debugging for Blazor doesn’t exist yet, but you’ll still be able to debug the whole application (assuming your server-side is using ASP.NET Core). - I can’t see my C# in the browser!
C# code in Blazor is compiled through WebAssembly before being delivered to the browser. When this happens, the C# can’t be displayed in the browser. However, you can still see the code in Chrome through remote debugging. Follow these steps. - Why isn’t my new route working?
Most of the time you’ll need to rebuild the application to get new routes on development applications. Other causes might be naming problems or a problem with the route parameter types. - Everything seems to be loading slow
This can be multiple issues, some of which are not Blazor-specific. However, for the Blazor-specific issues, it varies between server and client. Any page using server-side Blazor must make an HTTP call to the server, which deals a hit to performance. Any site using client-side Blazor will have a long initial load time, then be more relaxed later. - I’m seeing a blank page and I set everything up correctly!
This is a specific one that I ran into when first using the templates in Visual Studio 2019. The solution was making sure I had the right .NET Core SDK installed. You can have the wrong version and still create a Blazor website with no errors, at least until the app starts running. You can install the latest version of the .NET Core SDK here.
Online Resources
by JP Roberts III
As of the writing of this blog post, Blazor is still a new framework, and as such, is still changing rapidly. Pluralsight doesn’t have any courses covering Blazor, Udemy only has a couple of short videos, and Microsoft’s Learning site has no specific courses dedicated to Blazor.
However, there are several websites that have a good deal of information and samples for developers:
YouTube also has several informative videos on Blazor, such as a two-part series on the Microsoft Visual Studio channel: Blazor – Part 1 , Blazor – Part 2.