I have on ASP.Net C# web API with an endpoint for the import. Javascript client sends a list of items to this API and API process this list in another thread (long task) and immediately returns unique id (GUID) of process. Now I need the cancel the background task from the CLIENT. Is possible to somehow send the cancelation token from the client? I have tried to add CancellationToken as a parameter to my controller async action but I don’t know how to pass it from the client. For simplification, we can use as the client the Postman app.
Sample server-side
[HttpPost]
[UserContextActionFilter]
[RequestBodyType(typeof(List<List<Item>>))]
[Route("api/bulk/ImportAsync")]
public async Task<IHttpActionResult> ImportAsync()
{
var body = await RequestHelper.GetRequestBody(this);
var queue = JsonConvert.DeserializeObject<List<List<Item>>>(body);
var resultWrapper = new AsynckResultWrapper(queue.Count);
HostingEnvironment.QueueBackgroundWorkItem(async ct =>
{
foreach (var item in queue)
{
var result = await ProcessItemList(item, false);
resultWrapper.AddResultItem(result);
}
});
return Ok(new
{
ProcessId = resultWrapper.ProcessId.ToString()
});
}
private async Task<ItemResult> ProcessItemList(<List<Item>>itemList, bool runInOneTransaction = false)
{
try
{
var result = await PerformBulkOperation(true, itemList);
return new ResultWrapper(result);
}
catch (Exception ex)
{
// process exception
return new ResultWrapper(ex);
}
}
Answers:
Thank you for visiting the Q&A section on Magenaut. Please note that all the answers may not help you solve the issue immediately. So please treat them as advisements. If you found the post helpful (or not), leave a comment & I’ll get back to you as soon as possible.
Method 1
On a high level what you could do is store the process id along with a cancellation token source when you queue the work. Then you can expose a new endpoint that accepts a process id, gets the cancellation token source from the store and cancels the associated token:
[HttpPost]
[UserContextActionFilter]
[RequestBodyType(typeof(List<List<Item>>))]
[Route("api/bulk/ImportAsync")]
public async Task<IHttpActionResult> ImportAsync()
{
var body = await RequestHelper.GetRequestBody(this);
var queue = JsonConvert.DeserializeObject<List<List<Item>>>(body);
var resultWrapper = new AsynckResultWrapper(queue.Count);
HostingEnvironment.QueueBackgroundWorkItem(async ct =>
{
var lts = CancellationTokenSource.CreateLinkedTokenSource(ct);
var ct = lts.Token;
TokenStore.Store(resultWrapper.ProcessId, lts);
foreach (var item in queue)
{
var result = await ProcessItemList(item, ct, false);
resultWrapper.AddResultItem(result);
}
TokenStore.Remove(processId) // remove the cancellation token source from storage when doen, because there is nothing to cancel
});
return Ok(new
{
ProcessId = resultWrapper.ProcessId.ToString()
});
}
private async Task<ItemResult> ProcessItemList(<List<Item>>itemList, CancellationToken token, bool runInOneTransaction = false)
{
try
{
var result = await PerformBulkOperation(true, itemList, token);
return new ResultWrapper(result);
}
catch (Exception ex)
{
// process exception
return new ResultWrapper(ex);
}
}
[Route("api/bulk/CancelImportAsync")]
public async Task<IHttpActionResult> CancelImportAsync(Guid processId)
{
var tokenSource = TokenStore.Get(processId);
tokenSource.Cancel();
TokenStore.Remove(processId) // remove the cancellation token source from storage when cancelled
}
In the above example I modified the ProcessItemList to accept a cancellation token and pass it to PerformBulkOperation, assuming that method has support for cancellation tokens. If not, you can manually call ThrowIfCancellationRequested(); on the cancellation token at certain points in the code to stop when cancellation is requested.
I’ve added a new endpoint that allows you to cancel a pending operation.
Disclaimer
There are for sure some things you need to think about, especially when it is a public api. You can extend the store to accepts some kind of security token and when cancellation is requested you check whether it matches with the security token that queued the work. My answer is focused on the basics of the question
Also, I left the implementation of the store to your own imagination 😉
All methods was sourced from stackoverflow.com or stackexchange.com, is licensed under cc by-sa 2.5, cc by-sa 3.0 and cc by-sa 4.0