How to Use Cancellation Tokens in ASP.NET Core

By FoxLearn 2/3/2025 9:04:26 AM   154
ASP.NET Core 7, the latest version of Microsoft's open-source web application framework, brings many advanced features from previous .NET versions, one of which is cancellation tokens.

In ASP.NET Core applications, it’s important to make long-running operations (like database queries, file processing, or background tasks) cancelable. This can be done either by setting a time limit or based on user input, ensuring the application remains responsive and can release resources.

Understanding Cancellation Tokens

A CancellationToken is a lightweight object generated by a CancellationTokenSource. When the CancellationTokenSource is canceled, all associated CancellationToken consumers are notified, and the IsCancellationRequested property is set to true, signaling that the task should be canceled.

By passing a CancellationToken to a long-running operation, like an HTTP request, you enable the task to be stopped if the user cancels the operation (for example, by refreshing the browser or closing the tab). This prevents unnecessary resource usage.

Using Cancellation Tokens with Async Tasks

Cancellation tokens are also useful for async tasks. In this case, a cancellation request tells the task to stop what it’s doing. The task checks the token periodically, and if cancellation is requested, it halts its operation.

Handling Long-Running Requests in ASP.NET Core

In ASP.NET Core applications, you may often encounter scenarios where long-running tasks are required, such as querying databases or handling large files. When initiating such tasks, it’s essential to pass cancellation tokens to these operations and propagate cancellation requests appropriately.

If a task exceeds a set timeout or the user cancels a request, the cancellation notification is passed to the cancellation token. It is crucial that all long-running tasks respect this cancellation and stop execution to free up resources.

Listening for Cancellation Requests

Cancellation requests can be monitored by checking the IsCancellationRequested property of the CancellationToken, as shown below:

while(!cancellationToken.IsCancellationRequested)
{
    // Perform your task here
}

Alternatively, you can use the ThrowIfCancellationRequested() method to automatically throw an exception when cancellation is requested:

while(true)
{
    // Perform task here
    cancellationToken.ThrowIfCancellationRequested();
}

Another method is to register a callback with the CancellationToken:

WebClient webClient = new WebClient();   
cancellationToken.Register(() =>
{
    webClient.CancelAsync();
});

Creating a Minimal API Handler with Cancellation

Let’s now walk through an example where we simulate a long-running request and incorporate cancellation. First, we’ll implement a basic handler without cancellation:

app.MapGet("/hello", async () =>
{
    app.Logger.LogInformation("Request started at: " + DateTime.Now.ToLongTimeString());
    await Task.Delay(TimeSpan.FromSeconds(5));
    app.Logger.LogInformation("Request completed at: " + DateTime.Now.ToLongTimeString());
    return "Success";
});

When you hit the /hello endpoint, the handler runs completely even if the user tries to cancel the request by refreshing the page. The long-running task continues to completion regardless of user actions.

Adding Cancellation Token to the Handler

To handle cancellation, we inject the CancellationToken into the handler and pass it to the Task.Delay() method:

app.MapGet("/hello", async (CancellationToken token) =>
{
    app.Logger.LogInformation("Request started at: " + DateTime.Now.ToLongTimeString());
    await Task.Delay(TimeSpan.FromSeconds(5), token);
    app.Logger.LogInformation("Request completed at: " + DateTime.Now.ToLongTimeString());
    return "Success";
});

Now, when you refresh the browser during the request, the operation will stop immediately. You’ll notice the logs show the request was canceled before completing, and a TaskCancelledException is thrown.

Checking for Cancellation in ASP.NET Core

You can also check the cancellation state at any point by checking the IsCancellationRequested property of the CancellationToken. If the token has been canceled, it will return true. You can then throw an exception or handle the cancellation as needed:

if (token.IsCancellationRequested)
{
    app.Logger.LogInformation("Request has been canceled..");
    throw new OperationCanceledException();
}

Best Practices for Using Cancellation Tokens

Cancellation tokens are essential for operations that involve significant resources, like database calls or long-running background tasks. However, they aren’t always appropriate for controller actions that modify application state, as canceling these types of requests may cause inconsistency. Instead, they are best used for operations where stopping the process mid-way won’t lead to negative side effects, such as in resource-intensive background tasks or network calls.

In this article, we used Task.Delay() to simulate a long-running operation. However, you can substitute this with your own long-running processes, such as file handling, database queries, or HTTP requests.

Using cancellation tokens effectively ensures that your application remains responsive and efficient, especially when handling user-driven cancellations or timeouts.