Reputation: 2123
I'm running an Owin Self hosted web Api, with typical controller like so:
[HttpGet]
[Route("refresh/{secId}")]
[ResponseType(typeof(int))]
public async Task<IHttpActionResult> RefreshElement(int secId)
{
var count = await _db.Refresh(secId);
if (count == 0)
return NotFound();
return Ok(count);
}
Assuming _db.Refresh() is long running (few secs) and sometimes throws an exception.
The problem i've managed to reproduce is this:
The result of _db.Refresh is no longer awaited - because I see when it returns an exception, it shows up via the TPL unobserved exception handling when the task is GCd...
Maybe because of such interactions the .net team changed the unhandled exception policy not to tear down processes (4.5 onwards i think)... so what's a good pattern out of this problem? Specifically for self hosted WebApi using OWIN - because i still log unobserved exceptions as FATAL :)
I can make _db.Refresh() take in a cancellation token, but how/when do i set a cancellation token for disconnects / cancels in self host webapi?
Upvotes: 8
Views: 10521
Reputation: 79441
Perhaps this does not fully answer your question, but I believe it answers this last part:
I can make _db.Refresh() take in a cancellation token, but how/when do i set a cancellation token for disconnects / cancels in self host webapi?
If you add a CancellationToken
parameter to your action method, the framework will supply one when it calls it.
[HttpGet]
[Route("refresh/{secId}")]
[ResponseType(typeof(int))]
public async Task<IHttpActionResult> RefreshElement(
int secId, CancellationToken cancellationToken)
{
var count = await _db.Refresh(secId, cancellationToken);
if (count == 0)
return NotFound();
return Ok(count);
}
Note however that there is still a small window of opportunity in which the client could disconnect before the action method exits, in which case your TPL unobserved exception will still occur.
Upvotes: 14
Reputation: 149518
You can handle the request time out by implementing a middleware yourself.
The IOwinContext
has a Request
property which itself holds a CancellationToken
named CallCancelled
which can be polled to see if the request was canceled:
public class CancellationAwareOwinMiddleware : OwinMiddleware
{
public async Task Invoke(IOwinContext owinContext)
{
try
{
await Next.Invoke(owinContext);
}
catch (TaskCanceledException)
{
// Handle cancellation.
}
catch (Exception e)
{
if (owinContext.Request.CallCancelled.IsCancellationRequested)
{
// Handle cancellation.
}
}
}
}
Note the general exception clause is there because i'm not sure OWIN will throw a TCE when tge request is cancelled, so pole the token anyway to be sure.
Edit:
As @usr pointed out to me, this solution would handle the case of the outter Task
going unobserved, and that was a rather large assumption I was making. If it's the inner task, I'll need to rethink the solution.
Upvotes: 0