Photo by Mike van den Bos on Unsplash
In the recent three years I did quite a bit of web development also using Microsoft's new web framework called Blazor. Blazor adds component-first support to ASP.NET by introducing "Razor components". With Razor components Blazor is capable of providing a full single-page application framework.
One of the things that make Blazor attractive since .NET 6 is the introduction of hot-reloading. What is hot-reloading and how can we use it? Where are its limitations? In this article, we will explore the hot-reload feature in Blazor, how it works, and how it can make the development process faster and more efficient.
Hot-Reload Basics
Hot-reload is a feature that allows developers to modify code while an application is running without having to restart it manually. In other words, developers can make changes to their code and immediately see the impact of those changes in the running application without any downtime. This feature is a massive time-saver for developers since it eliminates the need to stop and start the application manually every time they make changes.
Blazor's hot-reload feature allows developers to modify their code in real-time, making it easier to debug and test applications. With hot-reload, developers can tweak their code and see the changes instantly, without having to waste time waiting for the application to restart.
There are some constraints and requirements that developers should be aware of when using the hot-reload feature in Blazor:
- Blazor hot-reload requires a supported browser: To use the hot-reload feature in Blazor, you need to use a browser that supports WebSockets, which is the communication protocol used by Blazor for hot-reload. Currently, most modern browsers, including Chrome, Firefox, Edge, and Safari, support WebSockets.
- Hot-reload is not enabled by default: To use the hot-reload feature in Blazor, you need to explicitly enable it in your project. This can be done by adding the
Microsoft.AspNetCore.Components.WebAssembly.DevServer
NuGet package to your project and modifying your project's launch settings to enable hot-reload. - There are some performance implications: While hot-reload is a valuable tool, it can have some performance implications, particularly when dealing with large applications. In some cases, the hot-reload process can cause the application to slow down or become unresponsive.
By keeping these constraints and requirements in mind, developers can use the hot-reload feature in Blazor more effectively and avoid any potential issues or performance problems.
Another thing to know about are the limitations of hot-reload.
Limitations of Hot-Reload
Although Blazor's hot-reload feature is a significant time-saver and an efficient tool for developers, it does come with some limitations.
First, it has limited support for changing the structure of the code: While Blazor's hot-reload feature is excellent for modifying the content of the code, it has limited support for making changes to the structure of the code. This means that any changes that affect the underlying structure of the code, such as renaming a class or adding a new property, may require a manual application restart.
Another limitation is the lack of support for changing the dependencies: Another limitation of Blazor's hot-reload feature is that it does not support changes to the application's dependencies. If the developer adds or removes a dependency, the application needs to be restarted manually.
Keep in mind that by hot-reloading you might be inable to preserve the application state: Blazor's hot-reload feature may cause the application to lose its state when the code is reloaded. This means that the developer may have to re-enter data or re-run certain processes every time they make changes to the code.
Finally, it has limited support for debugging: Although hot-reload is an efficient tool for developers to debug their code, it may not work in all scenarios. For instance, it may not be effective when debugging complex code, or when the changes made to the code have a significant impact on the application's behavior.
Overall, while Blazor's hot-reload feature is an essential tool, it's important to keep in mind its limitations and understand when a manual application restart may be necessary. If anything is fishy, just stop and restart as you'd have done it beforehand.
Internals of Hot-Reload
Blazor's hot-reload feature works by dynamically updating the application's code while it's running. When a developer makes changes to the code, the dotnet runtime monitors those changes and applies them to the running Blazor application in real-time.
The hot-reload feature works by using a combination of client-side and server-side technologies. The client-side component of the hot-reload feature is responsible for monitoring changes to the code, while the server-side component is responsible for applying those changes to the running application.
When a developer makes changes to the code, the client-side component of the hot-reload feature detects those changes and sends them to the server. The server then applies the changes to the running application without interrupting its execution. The server also compiles the modified code in the background to ensure that the changes are applied seamlessly to the running application.
Blazor's hot-reload feature uses a technique called IL (Intermediate Language) swapping to apply changes to the running application. IL swapping is a technique that replaces the existing IL code of a method with a new one. This technique allows the application to continue running without interruption, even when the code is being updated.
In order to find out if hot-reloading should be enabled a set of headers is necessary in the initial response of the boot.config.json. If the ASPNETCORE-BROWSER-TOOLS
header is found, then Blazor will activate a set of abilities.
In general, we find the following three custom headers attached to pretty much all static resources (but, as mentioned, for hot-reload only the boot.config.json request is relevant):
DOTNET-MODIFIABLE-ASSEMBLIES: debug
ASPNETCORE-BROWSER-TOOLS: true
Blazor-Environment: Development
How are these headers entering the process? Via the Blazor JavaScript. They are attached to the created bootConfig
, which is based mainly on the contents from the boot.config.json. Now having the ASPNETCORE-BROWSER-TOOLS
header will actually set the aspnetCoreBrowserTools
property. In turn, if this is truthy, then the __ASPNETCORE_BROWSER_TOOLS
environment variable will be set to its value. Finally, inside the .NET code running in WebAssembly this will be picked up and handled.
How specifically is this handled? Well, we have two different parts for this: A WebSocket connection to, e.g., ws://localhost:57151/YourProjectName/
which is established in the beginning. This one is used to monitor if changes have been made. Here, our running application will receive updates, for instance:
{
"type": "BlazorHotReloadDeltav1",
"sharedSecret": "...",
"deltas": [
{
"sequenceId": 5,
"moduleId": "ce3aed28-47b4-46e8-b475-9995359d01a2",
"metadataDelta": "...",
"ilDelta": "...",
"updatedTypes": [33554446, 33554456],
"pdbDelta": "..."
}
]
}
Interestingly, the type
is already versioned - this should ensure to support specifications of future .NET versions. Presumably, the capabilities as well as the format might change.
While the receiving part has modelled as a WebSocket connection, there is also a sending part. The sending part is a POST request to https://localhost:7094/_framework/blazor-hotreload
. Like beforehand, the blazor-hotreload
does not resolve to an existing or real resource. Instead, it needs to be provided by the debug runtime.
The format is similar to the messages received from the WebSocket:
[
{
"sequenceId": 5,
"moduleId": "ce3aed28-47b4-46e8-b475-9995359d01a2",
"metadataDelta": "...",
"ilDelta": "...",
"updatedTypes": [33554446, 33554456],
"pdbDelta": "..."
}
]
Indeed, this one is just sending back to the received / applied deltas
. This helps the .NET counter part to know what has been applied already - and what might be still missing.
The following diagram tries to place all these resources in an architecture:
So let's recap what URLs appeared here and what their roles have been:
/_framework/blazor-hotreload
: This URL is used to establish a connection between the Visual Studio side (client) and dev server side of the hot-reload feature. When a developer makes changes to the code, the client sends a request to this URL to notify the server of the changes./YourProjectName
: This URL is used to establish a WebSocket connection between the client-side and server-side components of the hot-reload feature. The WebSocket connection is used to facilitate real-time communication between the two components, allowing the server to send updated code to the client as soon as it's available.
This all results in the following sequence diagram:
In addition to these URLs, there may be other "magic" URLs and connections that are part of the Blazor hot-reload mechanism. These URLs and connections may be specific to the version of Blazor you're using or to the tooling you're using to develop your Blazor application.
Bonus: Browser Link
There are some other URLs that appear from time to time in a discussion about Blazor hot-reload: The /browserLink
and /_framework/aspnetcore-browser-refresh.js
URLs. While they are loosely related to the hot-reload feature, they serve a different purpose than the URLs from the previous section. However, for instance, the aspnetcore-browser-refresh.js
is responsible for establishing the WebSocket connection mentioned in the previous section.
Here's a brief explanation of each of these URLs and their role in the development process:
-
/browserLink
: This URL is used to enable the Browser Link feature in Visual Studio, which allows developers to interactively debug and modify their code in the browser. Browser Link can be used to perform tasks such as refreshing the browser, inspecting the DOM, and debugging JavaScript. -
/_framework/aspnetcore-browser-refresh.js
: This URL is used to enable browser refresh in Visual Studio, which allows developers to automatically refresh the browser when changes are made to the code. The aspnetcore-browser-refresh.js script is responsible for monitoring the server for changes and triggering a browser refresh when changes are detected. It also establishes the WebSocket connection to the hot-reload server, which is necessary to make this work in the first place.
While these URLs and features are not directly related to the hot-reload feature in Blazor, they can be used in conjunction with hot-reload to speed up the development process. For example, developers can use Browser Link to interactively debug and modify their code in the browser while using hot-reload to apply changes to the running application.
These URLs are entering our application by intercepting requests. Visual Studio will modify the response of a request if the accept
header is set to text/html
. In such scenarios the middleware injects the following piece of JavaScript code into the response:
const s1 = document.createElement('script');
s1.type = "text/javascript";
s1.src = "http://localhost:51554/ff.../browserLink";
s1.async = true;
s1.id = "__browserLink_initializationData";
s1.dataset.requestid = "6e3b...";
s1.dataset.requestmappingfromserver = "false";
s1.dataset.connecturl = "http://localhost:51554/ff.../browserLink";
document.body.appendChild(s1);
const s2 = document.createElement('script');
s2.type = "text/javascript";
s2.src = "http://localhost:5000/_framework/aspnetcore-browser-refresh.js";
s2.onload = () => {
if (typeof __wasmmodulecallback__ !== 'undefined') {
window.__wasmmodulecallback__();
delete window.__wasmmodulecallback__;
}
};
document.body.appendChild(s2);
Importantly, neither the browserLink
endpoint (handled by the Visual Studio instance) nor the aspnetcore-browser-refresh.js
file has to be provided by us as beforehand. The browserLink
and browser-refresh.js
give the IDE (e.g., Visual Studio) the ability to communicate with the browser. For instance, the console messages are then seen in Visual Studio, too. Additionally, breakpoints are synced and communicated such that the browser stops where it should.
Conclusion
Blazor's hot-reload feature is a useful tool that allows developers to make changes to their code and see those changes applied to the running application in real-time. This feature uses a combination of client-side and server-side technologies to monitor code changes and apply them on the fly, without interrupting the running application's execution. Blazor hot-reload uses a technique called IL swapping to apply changes to the running application, replacing the existing IL code of a method with a new one.
During the hot-reload process, you may encounter URLs such as /_framework/blazor-hotreload
and a WebSocket connection. These URLs and connections are part of the hot-reload mechanism and facilitate communication between the client-side and server-side components.
Blazor also offers other development tools, such as Browser Link and browser refresh, which can be used in conjunction with hot-reload to speed up the development process. While hot-reload has its limitations, such as not being able to handle all types of changes to the code, it is a valuable tool that can help developers save time and effort when building Blazor applications. Overall, hot-reload is a useful feature that demonstrates Blazor's commitment to delivering a smooth and efficient development experience.
Top comments (1)
Thanks for the great article!
I just have one update: In .NET 8, it is not necessary anymore to add a NuGet package in order to enable Hot Reload, you just need to click the hot reload button in VS or use "dotnet watch" in VS Code.