Introduction

Imagine you are developing an e-commerce application using ASP.NET Core Web API.

A customer opens the order history page, and your API starts fetching thousands of records from SQL Server.

But before the API finishes processing, the customer closes the browser or navigates to another page.

What happens to the API request?

Without proper cancellation handling, the server may continue executing the database query even though the customer no longer needs the response.

This means unnecessary CPU usage, database processing, memory consumption, and potentially wasted network resources.

This is where CancellationToken comes into the picture!

In this article, we will understand CancellationToken from scratch, explore how it works internally, and implement it in real-world ASP.NET Core applications.

1. What Is CancellationToken in C#?

CancellationToken is a .NET structure that allows developers to communicate a cancellation request to an ongoing operation.

In simple words, CancellationToken tells an operation: "The result is no longer required. Please stop if possible."

It is part of the System.Threading namespace.

CancellationToken is commonly used with:

  • ASP.NET Core Web APIs

  • Asynchronous database operations

  • HTTP requests

  • Background services

  • Long-running tasks

  • Microservices communication

Important Point

CancellationToken does not forcibly terminate a running thread or task.

Instead, it follows a cooperative cancellation mechanism.

This means the running operation must support and respond to cancellation requests.

2. Why Do We Need CancellationToken?

Let's understand the problem with a practical example.

Suppose our API has the following endpoint:

Code
GET /api/orders

The API fetches customer orders from SQL Server.

Before CancellationToken

  1. The client sends a request.

  2. The API starts executing a database query.

  3. The client closes the browser.

  4. The server may continue processing the query.

  5. The query completes, even though the response is no longer needed.

What Problems Can This Cause?

Unnecessary database load: SQL Server may continue processing queries that are no longer useful.

Resource wastage: Application and database resources remain occupied unnecessarily.

Reduced scalability: During high traffic, abandoned requests can compete with active requests for resources.

Higher infrastructure costs: Unnecessary processing may contribute to increased resource consumption.

After CancellationToken

  1. The client sends a request.

  2. ASP.NET Core associates a cancellation token with the request.

  3. The client disconnects.

  4. ASP.NET Core signals cancellation through the request token.

  5. Supported operations can observe the cancellation and stop early.

The result is that the application has an opportunity to release resources sooner.

3. How Does CancellationToken Work Internally?

CancellationToken works together with another .NET component called CancellationTokenSource.

Let's understand their responsibilities.

CancellationTokenSource

CancellationTokenSource is responsible for requesting cancellation.

CancellationToken

CancellationToken is passed to operations so they can observe cancellation requests.

Simple C# Example

C#
using System;
using System.Threading;
using System.Threading.Tasks;

using var cts = new CancellationTokenSource();

CancellationToken token = cts.Token;

// Request cancellation after 2 seconds
cts.CancelAfter(TimeSpan.FromSeconds(2));

try
{
    await Task.Delay(
        TimeSpan.FromSeconds(10),
        token);

    Console.WriteLine("Task completed");
}
catch (OperationCanceledException)
{
    Console.WriteLine("Task was cancelled");
}

Step-by-Step Explanation

Step 1: We create a CancellationTokenSource.

Step 2: We obtain a CancellationToken from the source.

Step 3: We pass the token to Task.Delay().

Step 4: After two seconds, the source requests cancellation.

Step 5: Task.Delay() observes the cancellation and throws an OperationCanceledException, typically a TaskCanceledException.

Step 6: The catch block handles the cancellation.

Key Concept

CancellationToken does not automatically stop arbitrary code.

An operation must either accept the token or explicitly check whether cancellation has been requested.

4. Using CancellationToken in ASP.NET Core Web API

Now let's implement CancellationToken in a real Web API.

Consider the following endpoint.

Without CancellationToken

C#
[HttpGet("orders")]
public async Task<IActionResult> GetOrders()
{
    var orders = await _context.Orders.ToListAsync();

    return Ok(orders);
}

This code executes an asynchronous database query.

However, we are not explicitly passing a cancellation token to Entity Framework Core.

If the client disconnects, the query is not informed of that cancellation through this method call.

With CancellationToken

C#
[HttpGet("orders")]
public async Task<IActionResult> GetOrders(
    CancellationToken cancellationToken)
{
    var orders = await _context.Orders
        .AsNoTracking()
        .ToListAsync(cancellationToken);

    return Ok(orders);
}

What Changed?

We added the following parameter:

Code
CancellationToken cancellationToken

ASP.NET Core automatically binds this parameter to the current request's cancellation token, exposed through HttpContext.RequestAborted.

We then pass the token to:

Code
ToListAsync(cancellationToken)

Now, if the request is aborted, Entity Framework Core can propagate cancellation to the database provider.

Important: Actual query cancellation depends on provider support and the stage of query execution. Cancellation is requested, not guaranteed to happen immediately.

5. What Is HttpContext.RequestAborted?

ASP.NET Core provides a built-in cancellation token called:

Code
HttpContext.RequestAborted

This token is signaled when the HTTP request is aborted, such as when the client connection is lost.

Example

C#
[HttpGet("products")]
public async Task<IActionResult> GetProducts()
{
    CancellationToken token =
        HttpContext.RequestAborted;

    var products = await _context.Products
        .AsNoTracking()
        .ToListAsync(token);

    return Ok(products);
}

Do We Need to Create CancellationTokenSource?

No.

For ordinary HTTP request cancellation, ASP.NET Core already provides the token.

We can simply accept CancellationToken as an action parameter and pass it to asynchronous operations.

6. Passing CancellationToken Across Application Layers

In real-world projects, developers often follow Clean Architecture or layered architecture.

For example:

Code
Controller → Service → Repository → Database

CancellationToken should be propagated through these layers.

Controller Layer

C#
[HttpGet("{id}")]
public async Task<IActionResult> GetOrder(
    int id,
    CancellationToken cancellationToken)
{
    var order = await _orderService.GetOrderAsync(
        id,
        cancellationToken);

    if (order == null)
        return NotFound();

    return Ok(order);
}

Service Layer

Code
public async Task<Order?> GetOrderAsync(
    int id,
    CancellationToken cancellationToken)
{
    return await _orderRepository.GetByIdAsync(
        id,
        cancellationToken);
}

Repository Layer

Code
public async Task<Order?> GetByIdAsync(
    int id,
    CancellationToken cancellationToken)
{
    return await _context.Orders
        .AsNoTracking()
        .FirstOrDefaultAsync(
            x => x.Id == id,
            cancellationToken);
}

Why Is This Important?

If the token is passed only to the controller but not to the repository, the database query will not receive the cancellation request through this method chain.

Best practice: Propagate CancellationToken through asynchronous operations that support cancellation.

7. CancellationToken vs CancellationTokenSource

These two concepts are closely related but have different responsibilities.

CancellationToken

CancellationTokenSource

Observes cancellation requests

Initiates cancellation requests

Passed to async operations

Creates and controls tokens

Cannot directly call Cancel()

Provides the Cancel() method

Lightweight struct

Disposable class

When Should We Use CancellationTokenSource?

Use it when your application needs to control cancellation.

Examples include:

  • Cancelling a background operation

  • Applying an application-specific timeout

  • Combining multiple cancellation signals

  • Stopping a long-running task

8. Using CancellationToken With HttpClient

Imagine your Order API communicates with a Payment API.

The Order API sends an HTTP request to the Payment API.

If the original client disconnects, should the Order API always continue waiting for the downstream response?

For read-only operations, often not.

Example

C#
public async Task<string> GetPaymentStatusAsync(
    CancellationToken cancellationToken)
{
    using var response = await _httpClient.GetAsync(
        "api/payments/status/101",
        cancellationToken);

    response.EnsureSuccessStatusCode();

    return await response.Content.ReadAsStringAsync(
        cancellationToken);
}

Passing the token allows the outgoing HTTP operation to respond to cancellation.

Important Production Consideration

Be careful when cancelling operations that modify business data.

For example, if a payment request has already reached another service, cancelling the HTTP wait does not guarantee that the payment operation was rolled back.

For payment, booking, or order creation workflows, use appropriate idempotency, transaction handling, and status reconciliation mechanisms.

9. CancellationToken With Timeout

Sometimes the client is still connected, but the operation is taking too long.

We can use CancellationTokenSource to apply a timeout.

C#
using var cts = new CancellationTokenSource(
    TimeSpan.FromSeconds(5));

try
{
    var result = await _httpClient.GetAsync(
        "api/products",
        cts.Token);
}
catch (OperationCanceledException)
    when (cts.IsCancellationRequested)
{
    Console.WriteLine("Operation timed out or was cancelled");
}

Here, cancellation is requested after five seconds.

Combining Request Cancellation and Timeout

In ASP.NET Core, we may want an operation to stop when either:

  • The client disconnects.

  • The application timeout is reached.

We can combine these conditions using linked tokens.

Code
using var cts =
    CancellationTokenSource.CreateLinkedTokenSource(
        HttpContext.RequestAborted);

cts.CancelAfter(TimeSpan.FromSeconds(5));

var products = await _context.Products
    .ToListAsync(cts.Token);

The linked token responds to both the request-aborted signal and the configured timeout.

10. ThrowIfCancellationRequested()

What if we are executing custom processing rather than calling an asynchronous framework method?

We can explicitly check the token.

C#
public async Task ProcessOrdersAsync(
    List<Order> orders,
    CancellationToken cancellationToken)
{
    foreach (var order in orders)
    {
        cancellationToken.ThrowIfCancellationRequested();

        await ProcessOrderAsync(
            order,
            cancellationToken);
    }
}

ThrowIfCancellationRequested() checks whether cancellation has been requested.

If cancellation is requested, it throws OperationCanceledException.

This is especially useful in loops and custom long-running operations.

For CPU-intensive processing, cancellation checks should be placed at appropriate intervals rather than assuming async automatically makes computation cancellable.

11. Common Mistakes Developers Make

Mistake 1: Not Passing the Token

Code
await _context.Orders.ToListAsync();

The operation does not receive the request cancellation token.

Better:

Code
await _context.Orders.ToListAsync(cancellationToken);

Mistake 2: Using CancellationToken.None

Code
await ProcessOrderAsync(
    order,
    CancellationToken.None);

This explicitly passes a token that cannot be cancelled.

Use it only when that behavior is intentional.

Mistake 3: Assuming Cancellation Stops Everything

CancellationToken does not forcibly terminate a task, thread, SQL query, or remote operation.

It signals cancellation, and the receiving operation must cooperate.

Mistake 4: Ignoring Business Consistency

Cancelling a request in the middle of an order creation workflow can create uncertainty about which operations have completed.

Design write operations with appropriate consistency guarantees.

Mistake 5: Swallowing Cancellation Exceptions

Avoid silently treating cancellation as an ordinary success response.

Handle expected cancellation separately from genuine application failures, and avoid misleading error logs.

12. Best Practices for Senior .NET Developers

When building production-ready APIs, follow these principles:

  1. Accept CancellationToken in asynchronous controller actions.

  2. Propagate it through services, repositories, and supported external API calls.

  3. Use HttpContext.RequestAborted for request-scoped cancellation.

  4. Apply explicit timeouts for operations with latency limits.

  5. Distinguish client cancellation from server-side timeouts.

  6. Avoid unnecessary work after cancellation has been requested.

  7. Handle cancellation exceptions appropriately.

  8. Never assume cancelling a request reverses completed database or remote operations.

  9. Use idempotency and consistency mechanisms for critical write workflows.

  10. Measure the impact of cancellation using logs, metrics, and tracing.

13. Frequently Asked Interview Questions

Q1. Does CancellationToken Kill a Running Thread?

No. It uses cooperative cancellation. The operation must observe the cancellation request.

Q2. Does CancellationToken Improve API Performance?

It can improve resource efficiency by reducing unnecessary work, especially for abandoned requests. It does not inherently make individual operations execute faster.

Q3. Is CancellationToken Mandatory With async/await?

No. But passing a token is recommended when an asynchronous operation supports cancellation and there is a meaningful cancellation signal.

Q4. Does EF Core Support CancellationToken?

Yes. Async methods such as ToListAsync(), FirstOrDefaultAsync(), and SaveChangesAsync() accept cancellation tokens. Actual cancellation behavior depends on the database provider.

Q5. What Happens if a User Closes the Browser?

ASP.NET Core may signal cancellation through HttpContext.RequestAborted when it detects the client disconnect. Operations that observe the token can then stop.

Q6. Can We Use CancellationToken in Microservices?

Yes. We can propagate cancellation to supported downstream calls. However, cancelling a caller's wait does not automatically undo operations already accepted by another service.

Summary

CancellationToken is an important feature for building responsive and resource-efficient ASP.NET Core applications.

It helps developers avoid unnecessary work when requests are abandoned, operations exceed their time limits, or processing is intentionally stopped.

The most important lesson is that CancellationToken does not make your API faster by itself. It helps your application avoid spending resources on work that may no longer be needed.

For senior backend developers, understanding CancellationToken is not just about passing a parameter. It is about designing applications that handle cancellation, timeouts, failures, and business consistency correctly.

#DotNet #AspNetCore #CancellationToken #WebAPI #CSharp #BackendDevelopment #AsyncAwait #Microservices #SoftwareEngineering