I have an OWIN-based ASP.NET Web API hosted in a Windows Service. Most of my ApiController
actions are async, and accept CancellationToken
parameters:
[Route("data/{id}")]
public async Task<IHttpActionResult> GetSomeDataAsync(int id, CancellationToken token)
{
try
{
using (var _dataSource = ...)
{
return Ok(await _dataSource.GetDataAsync(id, token));
}
}
catch (OperationCanceledException ex)
{
return StatusCode(HttpStatusCode.NoContent);
}
}
Using the built-in request-cancellation features of Web API, if the client cancels the request, token
is signaled and _dataSource
handles it appropriately and throws the OperationCanceledException
.
So far, so great.
But when my host process terminates (that is, the Windows Service stops), token
isn't signaled and the cancellation-and-bail-out process isn't graceful.
I'm aware of the OWIN environment dictionary's host.onAppDisposing property, and I've dug into the source for the Microsoft.Owin[.*] and Microsoft.AspNet.WebApi.* packages to try and figure out where GetSomeDataAsync
's token
argument is coming from, but I'm not sure how to connect the pieces together.
I'd like to do something like
class WebServiceInAWindowsService : ServiceBase
{
private readonly CancellationTokenSource _cts = new CancellationTokenSource();
...
protected override void OnStop()
{
_cts.Cancel();
}
}
But I'm not sure how to get _cts
to be the source of the CancellationTokens
that get fed to my actions, while not breaking the request-cancellation feature that's working well.
I'm thinking that CancellationTokenSource.CreateLinkedTokenSource()
might be useful, but I'm not seeing how to put the pieces together.
Can you help? Thanks!
host.onAppDisposing
is triggered when you call Dispose
on the value returned from WebApp.Start
.
GetSomeDataAsync
's is only associated with the request disconnect token by default (eg owin.CallCancelled
). Via middleware or otherwise you can replace it with a linked TCS that's also connected to host.onAppDisposing
.
Something like:
app.Use(async (env, next) =>
{
var reqAbt = env.Get<CancellationToken>("owin.CallCancelled");
var appAbt = env.Get<CancellationToken>("host.onAppDisposing");
using (linked = CancellationTokenSource.CreateLinkedTokenSource(reqAbt, appAbt))
{
env["owin.CallCancelled"] = linked.Token;
await next();
env["owin.CallCancelled"] = reqAbt;
}
});
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.