Since no one here gave a better answer I'll do it myself.
The code I provided in the question is, in my opinion, most cohesive and also general enough to be recycled in other cases.
TLDR;
Simply pass the token to all layers that may need it, and in each layer check whether IsCancellationIsRequested is true.
Long... requires some thought across sessions and requests...
On the request side, you need to have some sort of key. This key could be a GUID or some other unique, munged ID associated with the request. This is quite literally the key to everything that a particular user may do soon. This key (string) must map back to a . It can be stored in CancellationTokenSource or some other ephemeral store associated back to your user.HttpContext.Session
This means a few things.
Start the (possibly cancellable) Web API request (Db access or whatever) as an awaitable task, and, using this keyed task (This should run in the backgound), immediately return a HTTP or DELETE response with the cancelled task's info (it wasn't clear from your question whether the operation was a delete or a modify... I'm wording this as a delete, but the principle should be the same).POST
With this key, you've got it in the session state, and you can pass it on through any other layers of your solution. For example, you can
This cancellation token can be propagated to any or all layers of your software solution (see: https://devblogs.microsoft.com/premier-developer/recommended-patterns-for-cancellationtoken/).
Cancellation is done, on demand, by the user or a system call; in your case that could be a simple button click. At that point, any calls on any threads that check for cancellation will cancel their operations (if you so code, and you should). Why/how? You literally pass this token through every layer as part of a constructor call or other method, and in every layer check to see if the operation is cancelled (cooperative) or skip the checks and handle OperationCancelledOperation.
You are passing the cancellation token as the parameter object; that won't work.
The first async methods in dapper did not expose a cancellation token; when I tried to add them as an optional parameter (as a separate overload, to avoid breaking existing assemblies), things got very confused with "ambiguous method" compilation problems. Consequently, I had to expose this via a separate API; enter :CommandDefinition
val = (await conn.QueryAsync<int>(
new CommandDefinition(query, cancellationToken: tokenSource.Token)
).FirstOrDefault();
This then passes the cancellation-token down the chain to all the expected places; it is the job of the ADO.NET provider to actually use it, but; it seems to work in most cases. Note that it can result in a rather than an SqlException if the operation is in progress; this again is down to the ADO.NET provider, but makes a lot of sense: you could have interrupted something important; it surfaces as a critical connection issue.OperationCancelledException
As for the questions:
Why is the snippet completely buildable assuming that there is no compiler error on the whole solution?
Because... it is valid C#, even if it doesn't do what you expect.
Forgive me as I cannot test if calling tokenSource.Cancel() would really cancel the method because I don't know how to generate long running sql query. Will the .Cancel() really cancels the method and throws OperationCancelledException?
ADO.NET provider-specific, but yes it usually works. As an example of "how to generate long running sql query"; the command on SQL server is somewhat useful here, and is what I use in the integration tests.waitfor delay
I think you are thinking in a great way, I do not think you need to regret or repent.
This is a great idea, I also thought about it, and I implement my own solutionpublic abstract class RequestCancellationBase
{
public abstract CancellationToken Token { get; }
public static implicit operator CancellationToken(RequestCancellationBase requestCancellation) =>
requestCancellation.Token;
}
public class RequestCancellation : RequestCancellationBase
{
private readonly IHttpContextAccessor _context;
public RequestCancellation(IHttpContextAccessor context)
{
_context = context;
}
public override CancellationToken Token => _context.HttpContext.RequestAborted;
}
and the registration should be like this
services.AddHttpContextAccessor();
services.AddScoped<RequestCancellationBase, RequestCancellation>();
now you can inject wherever you want, RequestCancellationBase
CancellationToken this is because of public static implicit operator CancellationToken(RequestCancellationBase requestCancellation)this solution helped me, hope it is helpful for you also
As the documentation states, you need to call the method from the token source, not the token itself. Note the example code in the CancellationToken Struct documentation:Cancel()
// Define the cancellation token.
CancellationTokenSource source = new CancellationTokenSource();
CancellationToken token = source.Token;
...
source.Cancel();
how can I, in possession of only a CancellationToken, cancel it?
Without a reference to the source you cannot cancel the token, this is by design.
As a flawed workaround, when given a , you can create a new instance of the token source, assign its token to the provided token, and cancel the new source:CancellationToken
// Define the cancellation token.
CancellationTokenSource newSource = new CancellationTokenSource();
existingToken = newSource.Token;
...
newSource.Cancel();
// "existingToken" is cancelled hereafter
...but this will only affect downstream consumers of the token. Any entities with the token prior to updating the reference will still have the original, uncancelled token.
But do note that if you're creating the token to track tasks, then you do have the source, so this shouldn't be an issue.
Microsoft should have been more careful with the documentation. It is confusing to describe the CancellationTokens as events to be "triggered".
I'm certainly not a C# language expert so hopefully the community will let us know if I'm missing import points. But let me take a stab a few things...
So your question: are they equivalent.. only in the sense that they both provide a way to register and invoke callbacks. But for a lot of other reasons, no.
CancellationTokens are wrapper around CancellationTokenSource. They tie into the Task implementation. They are thread safe. They can be invoked externally and can be invoked by a timer. You can chain multiple CancellationTokenSource together. They maintain state which indicates if they are, or are not, canceled and you can query that state.
C# events, the language feature, are in the words of the documentation a special multicast delegate. They can only be invoked within the class where they are declared. They are not really thread safe. They are baked into XAML and WPF.
I'm not going to comment too much on where one is superior to the other as they are very different. In the normal course I don't think you'd consider events in situations where you would consider CancellationTokens. They overlapped in the case of IHostApplicationLifetime mainly because of bad documentation and the redesign of the hosting infrastructure for .NET Core. As @EricLippert mentioned this link provides a great overview of that.