Could a server application architecture that promises to provide an alternative to JavaScript be the next big thing for .NET developers in 2019? In this article we'll discuss Razor Components, a new framework that promises JavaScript free web development backed by .NET technologies like C#, .NET Standard, and SignalR.
Could a server application architecture that promises to provide an alternative to JavaScript be the next big thing for .NET developers in 2019? In this article we'll discuss Razor Components, a new framework that promises JavaScript free web development backed by .NET technologies like C#, .NET Standard, and SignalR. This new technology is on the road-map for .NET Core 3.0 and it's showing a lot of potential.
Razor Components, is what originally started as Blazor Server-Side in early 2018. Blazor is a .NET single page web application framework that is generally associated with .NET running on Web Assembly. However, Blazor is capable of running under multiple scenarios including server-side as a .NET Standard application, a.k.a. Razor Components. Blazor and Razor Components are essentially the same, except for where they execute. Blazor runs on the browser using web assembly, while Razor Components runs on the server. Razor Components treats the browser as a “thin-client” where the web browser handles just the user interface (UI) and the server receives events and responds with UI updates remotely. To the user, the application is indistinguishable from any other web application.
Author's note: In my opinion, the current name choice of “Razor Components” leads to confusion. To clarify what we're discussing:
- Razor is a template markup syntax for .NET
- Blazor is a .NET based web framework which can run on the client using WebAssembly or as:
- Razor Components - Blazor framework running on the server
On the surface the concept of Razor Components brings up more questions that it answers, so let's dig deeper into the architecture.
Server - Client Architecture
Razor Components has a unique architecture that allows a .NET application to run server-side while the user interface runs out of process in a remote browser. The combination of .NET Core and SignalR allows us to write web applications without JavaScript.
At the “Core”
Razor Components is the combination of several .NET Core technologies. First and foremost a Razor Components application utilizes .NET Standard 2.0. Building with .NET Standard means Razor Components has a large .NET API surface to work with as it is compatible with other .NET Standard libraries. In addition, Razor Components is a .NET Core application, so it can be built and deployed using any operating system. A quick look inside a Razor Components .csproj
shows we're in familiar .NET territory. The application type is even an EXE
, which tells us this is an executable. > In .NET Core the actual output is an executable .dll, not a literal .exe.
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<OutputType>Exe</OutputType>
<LangVersion>7.3</LangVersion>
<!-- ... -->
</PropertyGroup>
So what do we do with this executable and how does it translate into a magical thin-client enabled web application? To understand this we'll need to take a look at the server & client relationship.
Now Serving Razor Components
On the server the application is hosted by ASP.NET Core. This is again a typical ASP.NET Core application, the base on which other ASP.NET Core apps are hosted including: MVC, WebAPI, and Razor Pages. In a Razor Components application the host uses services and middleware to register the application in the HTTP pipeline. Looking into the Startup.cs
of the server application we can see the services and middleware configurations.
At the time of writing, Razor Components is still called
ServerSideBlazor
in the related APIs.
public void ConfigureServices(IServiceCollection services)
{
// ...
// adds the Server-Side Blazor services, and those registered
// by the app project's startup
services.AddServerSideBlazor<App.Startup>();
// ...
}
// This method gets called by the runtime. Use this method to
// configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app,
IHostingEnvironment env)
{
// ...
// Use component registrations and static files from the
// app project.
app.UseServerSideBlazor<App.Startup>();
// ...
}
Once the server application is ready we can make an initial web request request, this leads us to setting up the browser as a “thin-client”.
SignalR the Thin-Client
Upon making a request to the server, we receive back static files for the application. As with most typical web applications we receive HTML. With Razor Components the first file received by the client is the Index.html
file which kicks off the client-side application. The client application is a single JavaScript file included with the framework, blazor.server.js
. This application is a special SignalR client with a responsibility to receive/process DOM changes from the server, send events to the server, and most importantly establish a WebSocket connection with the server.
The communication between the client and server is done through SignalR as binary data. The packets of data contain just the necessary DOM changes and not the entire rendered DOM. There's NO Viewstate a la ASP.NET WebForms to worry about with Razor Components.
With the client - server relationship understood, now we can focus on the UI portion of the framework.
Writing Web UIs Razor and C
Since Razor Components runs server side as a .NET Standard app, logic is written using .NET technologies. This is possible due to the Blazor framework which employs the RenderTree
, a DOM abstraction similar to virtual DOMs used in popular JavaScript frameworks like Angular and React. Let's look at UI side of the framework to understand how components are written.
Component Basics
Razor is the popular template markup syntax that was introduced in ASP.NET MVC 3.0. Razor gained popularity through its simplicity in that it requires an @
symbol to begin a code-block without an explicit closing tag. Unlike the MVC framework, where Razor is used to explicitly write HTML, Razor Components emits a RenderTree. In Razor Components every .cshtml
file is as a component which can be used as an element, ex: Widget.cs
becomes <Widget>
. Carrying on the tradition of simplicity can be seen in the following image as much of the concept of a component can be understood from some basic annotations.
The header of a component defines special characteristics or features of a component such as: routing, layout, and dependency injection. In the image, we can see that this component has a route specified by @page "/counter"
. The component acts as a page when visiting the route /component
, additionally the component can be used in other components as the element <Counter>
.
All markup inside the component becomes part of the RenderTree. This shares some similarity to the way .jsx
is used in React. Given the example <h1>Counter</h1>
the framework will create the following code behind the scenes when building the RenderTree builder.AddMarkupContent(0, "<h1>Counter</h1>\n\n");
. With the RenderTree abstracted away, developers can focus on writing components with Razor and HTML.
Component logic is written as C# within a @funcitons
block. Fields, properties, and events are bound using Razor syntax. Referencing an event or value from the code block within the component's markup is as simple as using @propertyName
.
Component Parameters
The concept of components goes beyond encapsulating markup. In order to create reusable components we need to be able to communicate with components. Components support the use parameters to bind data and events. Using the [Parameter]
attribute in a component exposes properties for consumption. In the following Counter component, the [Parameter]
attribute is added to allow consumers to specify the amount to increment the count by.
<p>Current count: @currentCount</p>
<button class="btn btn-primary" onclick="@IncrementCount">
Click me
</button>
@functions {
int currentCount = 0;
[Parameter] protected int CountBy { get; set; } = 1;
void IncrementCount()
{
currentCount += CountBy;
}
}
In another file we can now set the CountBy
value on the component element.
<Counter CountBy="5"></Counter>
The parameter CountBy
can also be bound to other components on the page allowing the value to be set dynamically by the user. For example, we can place an input on the page and bind its value to the CountBy
property. Now the counter's behavior can change at run-time by changing the value of the input.
<Counter CountBy="@countBy"></Counter>
<input bind="@countBy" type="number" />
@functions {
int countBy = 5;
}
The component model is easy to learn but provides a robust base to build complex UIs. Additional libraries can also be used to support the application logic and UI experience.
A Broad Range of Options: Including JavaScript
Because Razor Components leverages .NET and the browser, the architecture allows for some interesting integration points.
.NET Standard and Beyond
Since Razor Components is a .NET Core application, it works with .NET Standard libraries, this means existing NuGet packages and SDKs may be compatible. This includes most .NET Standard libraries provided they do not interact with or depend on technologies that are tightly coupled to a platform. For example, a library like MarkDig which is used to parse Markdown into HTML is compatiable because it has no dependencies beyond .NET Standard 2.0. At the time of writing, even the advanced .NET based Machine Learning SDK - ML.NET is compatible when used as a service within a Razor Components application.
JavaScript Interoperability
Additionally a Razor Components app can use dependencies the JavaScript ecosystems and through an interoperability layer the app can communicate bidirectionally with both .NET and JavaScript dependencies. This is helpful for situations where the Razor Components does not support a necessary browser/DOM API or an existing JavaScript library is useful.
While the component model in Razor Components an solve most UI needs, some DOM APIs are still needed. For example, getting the users geo location information is not supported directly through Razor Components. To access this feature as a DOM API, Razor Components can call upon the JavaScript implementation in the browser through the JavaScript interop layer.
The following example shows an example of how a C# abstraction can be written to interface with the browser's geo location APIs, specifically the getCurrentPosition
method. From .NET, the JSRuntime.Current.InvokeAsync
method is used to invoke a JavaScript module interopGeolocation.js
.
// GeoLocation.cs (.NET)
public class Geolocation
{
// ...
public async Task GetCurrentPosition(
Action<Position> onSuccess,
Action<PositionError> onError,
PositionOptions options = null)
{
OnGetPosition = onSuccess;
OnGetPositionError = onError;
await JSRuntime.Current.InvokeAsync<bool>(
"interopGeolocation.getCurrentPosition",
new DotNetObjectRef(this),
options);
}
// ...
}
Through invokeMethodAsync
the JavaScript module is able to call back into .NET returning the results.
// interopGeolocation.js (Browser)
window.interopGeolocation = {
getCurrentPosition: function (geolocationRef, options) {
const success = (result) => {
geolocationRef.invokeMethodAsync(
'RaiseOnGetPosition',
interopGeolocation.toSerializeable(result));
};
const error = (er) =>
geolocationRef.invokeMethodAsync(
'RaiseOnGetPositionError',
er.code);
navigator.geolocation.getCurrentPosition(
success,
error,
options);
},
// ...
The abstraction is then used within the application as normal C# code. This interop can written as a .NET library that can be shared among many projects.
// Index.cshtml (Application usage)
// Component initialized
protected override async Task OnInitAsync()
{
var g = new Geolocation();
await g.WatchPosition(Handler, HandleError);
}
void Handler(Position p)
{
Lat = p.Coords.Latitude;
Long = p.Coords.Longitude;
}
Ideally, once an interop is created for an API, it shouldn't have to be created again.
ASP.NET Core 3.0 Timeline
Razor Components was first announced during DotNET Conf. It is expected that Razor Components will ship with ASP.NET Core 3.0 mid-2019. The following quote from Daniel Roth explains what the goal of Blazor and Razor Components will be over the near term.
Our primary goal remains to ship support for running Blazor client-side in the browser. Work on running Blazor client-side on WebAssembly will continue in parallel with the Razor Components work, although it will remain experimental for a while longer while we work through the issues of running .NET on WebAssembly. We will however keep the component model the same regardless of whether you are running on the server or the client. You can switch your Blazor app to run on the client or the server by changing a single line of code. See the Blazor .NET Conf talk to see this in action and to learn more about our plans for Razor Components:
Getting Started
If you're the type of developer who likes to test things early, you can try Blazor Components today. At the time of writing, Razor Components is still being branded as Server-Side Blazor which can be found at Blazor.NET. Follow the instructions under getting and be sure to choose the “Blazor (Server-Side in ASP.NET Core)” project type.
Pros & Cons
Since only a small amount of JavaScript is required to bootstrap the client and no .NET assemblies are transferred to the client. Even during operation network traffic is light because communication between the browser “thin-client” and the server is a small binary packet. However, because web sockets are used, the client and server must always be connected. This means that Razor Components apps cannot work in offline mode. If offline mode is a must for your application, then the WebAssembly equivalent Blazor application may be a better choice. With that said, Blazor has yet to receive an official release date or promise of support from Microsoft.
Scale with Azure
One last point of concern to some may be the question of “how will Razor Components scale.” Since Razor Components is built with SignalR, it has the advantage of Azure. Azure SignalR Service is a fully managed service infrastructure that works seamlessly with ASP.NET Core SignalR. Integrating SignalR Service requires very little code modification to add automatic capacity provisioning, scaling, or persistent connections.
Wrapping up
Razor Components, the server-side implementation of the Blazor framework is a new take on web development. By utilizing the web browser as a thin-client Razor Components offers a new alternative to JavaScript frameworks. Its component model is simple enough that it maintains a short learning curve, yet is robust enough to be extremely productive. Even though Razor Components is new, its ability to use existing .NET libraries, tooling and optionally JavaScript's ecosystem as well makes it a strong candidate for front end web development.
Top comments (1)
Nice article. Thank you.