Canceling background tasks when a user navigates away from a Blazor component
In an application, you don't want to waste resources. When an ongoing operation is not needed anymore, you should cancel it. For instance, if your application downloads data to show them in a view, and the user navigates away from this view, you should cancel the download.
In a .NET application, the way to cancel operation is to use a CancellationToken
. You can create a CancellationToken
using the CancellationTokenSource
. Once the Blazor component is removed from the page, you can call the Cancel
method to stop the ongoing operations.
According to the Blazor component lifecycle, you can call the Cancel
method in the IDisposable.Dispose()
method.
@page "/fetchdata"
@using System.Threading
@implements IDisposable
@inject HttpClient Http
... todo content
@code {
// 👇 Create the CancellationTokenSource
private CancellationTokenSource cts = new CancellationTokenSource();
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
// 👇 Use the CancellationToken for the http call using cts.Token
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json", cts.Token);
}
// 👇 Cancel the token
public void Dispose()
{
cts.Cancel();
cts.Dispose();
}
public class WeatherForecast
{
public DateTime Date { get; set; }
public int TemperatureC { get; set; }
public string Summary { get; set; }
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
}
}
#Using a base component
Instead of adding a CancellationTokenSource
to all components manually, you can create a base component that expose a CancellationToken
and use this base component automatically in all components of the project.
public abstract class ComponentWithCancellationToken : ComponentBase, IDisposable
{
private CancellationTokenSource? _cancellationTokenSource;
protected CancellationToken ComponentDetached => (_cancellationTokenSource ??= new()).Token;
public virtual void Dispose()
{
if (_cancellationTokenSource != null)
{
_cancellationTokenSource.Cancel();
_cancellationTokenSource.Dispose();
_cancellationTokenSource = null;
}
}
}
Then, edit the _Imports.razor
files with:
@inherits ComponentWithCancellationToken
@using System.Net.Http
@using Microsoft.AspNetCore.Authorization
@using Microsoft.AspNetCore.Components.Authorization
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.JSInterop
@using MyBlazorApp
@using MyBlazorApp.Shared
You can now use the ComponentDetached
property in your components:
@inject HttpClient Http
... todo content
@code {
private WeatherForecast[] forecasts;
protected override async Task OnInitializedAsync()
{
forecasts = await Http.GetFromJsonAsync<WeatherForecast[]>("sample-data/weather.json", ComponentDetached);
}
}
Do you have a question or a suggestion about this post? Contact me!