57

There are different examples for async controllers. Some of them use CancellationToken in method definition:

public async Task<ActionResult> ShowItem(int id, CancellationToken cancellationToken)
{
    await Database.GetItem(id, cancellationToken);
    ...

But other examples and even the default ASP.NET projects for VS2013 don't use CancellationToken at all and work without it:

public async Task<ActionResult> ShowItem(int id)
{
    await Database.GetItem(id);
    ...

It's not clear, if we should use CancellationToken in controllers or not (and why).

i3arnon
  • 113,022
  • 33
  • 324
  • 344
user1224129
  • 2,759
  • 3
  • 27
  • 29

3 Answers3

58

You should use it. Right now it only applies if you have an AsyncTimeout, but it's likely that a future MVC/WebAPI version will interpret the token as "either timeout or the client disconnected".

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810
  • 1
    Has the situation with Web API changed with respect to cancellation tokens and disconnected clients? – Drew Noakes Apr 24 '15 at 15:27
  • @DrewNoakes: AFAIK it has not yet. I believe this will be fixed in ASP.NET vNext. – Stephen Cleary Apr 24 '15 at 22:26
  • 17
    I just tested it under VS 2015 and hitting stop in the browser did trigger the cancellation token. – Jonathan Allen Jul 30 '15 at 09:09
  • How would you pass a Cancellation Token from a C# Web Client to a Get API method? How do you serialize the cancellation token into the URL string? – eesh Oct 01 '15 at 14:53
  • 1
    @eesh: I wouldn't try that at all. Instead, consider having the client close its connection when a cancellation token is triggered; and having the server take a cancellation token as an argument (which is automatically supplied by WebAPI and canceled when the connection is dropped). – Stephen Cleary Oct 01 '15 at 16:26
  • In this case cancellation token behavior depends on framework version and server implementation. See [CancellationToken](https://github.com/aspnet/Mvc/issues/5239) issue in ASP.NET MVC GitHub repository. – Leonid Vasilev Jan 11 '18 at 13:20
  • I tested it (F5, Esc) on React App and it works with Firefox and it does not work with Chrome and Edge. – Tomas Kubes May 23 '19 at 07:10
  • So Chrome and Edge are incorrectly keeping the connection to the server open AFTER the tab/window is closed? – Triynko Aug 10 '20 at 20:55
  • @Triynko: I'm not sure why that would be incorrect behavior, assuming HTTP 1.1 or newer. – Stephen Cleary Aug 10 '20 at 21:03
  • Idk why this ever would have worked, but I'm testing now in VS 2022 with MVC 4 app, and the CancellationToken is definitely not triggered by a client cancellation. – Bryan Williams Sep 06 '22 at 20:16
  • @BryanWilliams: There was a bug in the .NET Framework `CancellationToken` implementation for client cancellation that could cause actual application crashes. IIRC, ASP.NET Core was already planned and fixing the bug was deemed difficult, so the team just turned off that code path. So, on .NET Framework, `CancellationToken` will not be triggered by client cancellation, and it's going to stay that way. I believe this does work on modern Core (MVC 5), though I'm not sure if it worked from v1 or when exactly it was added. – Stephen Cleary Sep 06 '22 at 21:23
8

You could use this

public async Task<ActionResult> MyReallySlowReport(CancellationToken cancellationToken)
{
    CancellationToken disconnectedToken = Response.ClientDisconnectedToken;
    using (var source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, disconnectedToken))
    {
        IEnumerable<ReportItem> items;
        using (ApplicationDbContext context = new ApplicationDbContext())
        {
            items = await context.ReportItems.ToArrayAsync(source.Token);
        }
        return View(items);
    }
}

taken from here.

Mikael Dúi Bolinder
  • 2,080
  • 2
  • 19
  • 44
Ram Y
  • 1,944
  • 17
  • 23
  • 2
    You MUST dispose LinkedTokenSource because it will never be collected by GC and it will cause memory leak. MSDN: Notice that you must call Dispose on the linked token source when you are done with it. http://msdn.microsoft.com/en-us/library/dd997364.aspx – Miles Sep 04 '17 at 11:00
  • According to the reference, this process is required to make the cancellation token work with MVC 5; "...for some reason, MVC 5 only supports cancellation if you use the AsyncTimeout attribute.". The article points out that "Everything works as expected in both ASP.NET Core MVC and in Web API...". i.e. If you're using .NET Core then you need only pass the CancellationToken through as normal. – SeanStanden Feb 03 '20 at 01:04
8

Users can cancel requests to your web app at any point, by hitting the stop or reload button on your browser. Typically, your app will continue to generate a response anyway, even though Kestrel won't send it to the user. If you have a long running action method, then you may want to detect when a request is cancelled, and stop execution.

You can do this by injecting a CancellationToken into your action method, which will be automatically bound to the HttpContext.RequestAborted token for the request. You can check this token for cancellation as usual, and pass it to any asynchronous methods that support it. If the request is cancelled, an OperationCanceledException or TaskCanceledException will be thrown.

Below link explains this scenario in detail.

https://andrewlock.net/using-cancellationtokens-in-asp-net-core-mvc-controllers/

NidhinSPradeep
  • 1,186
  • 2
  • 14
  • 15