5

I have a base controller with the following abstract method:

[HttpDelete]
public abstract Task<IHttpActionResult> Delete(int id);

In one particular controller, I don't want to implement deletion, so the method looks like this:

public override async Task<IHttpActionResult> Delete(int id)
{
    return ResponseMessage(Request.CreateResponse(HttpStatusCode.MethodNotAllowed, new NotSupportedException()));
}

Although the above code compiles, I get a warning:

This async method lacks 'await' operators and will run synchronously. Consider using the 'await' operator to await non-blocking API calls, or 'await Task.Run(...)' to do CPU-bound work on a background thread.

Apart from ignoring the above warning, is there a better alternative (ie. changing the code above) so that this warning doesn't occur?

EDIT

I change the line to:

return await Task.Run(() => ResponseMessage(Request.CreateResponse(HttpStatusCode.MethodNotAllowed, new NotSupportedException())));

This removes the warning. However, is there a better solution?

Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
Ivan-Mark Debono
  • 15,500
  • 29
  • 132
  • 263

2 Answers2

6

Apart from ignoring the above warning, is there a better alternative (ie. changing the code above) so that this warning doesn't occur?

The alternative is to remove the async modifier and use Task.FromResult to return a Task<IHttpActionResult>:

public override Task<IHttpActionResult> Delete(int id)
{
    return Task.FromResult<IHttpActionResult>(
                ResponseMessage(Request.CreateResponse(
                                        HttpStatusCode.MethodNotAllowed,
                                        new NotSupportedException())));
}
Yuval Itzchakov
  • 146,575
  • 32
  • 257
  • 321
6

While Yuval's answer regarding removing the async completely is usually the prefered way to remove the warning, another correct answer that doesn't degrage performance is to await an already completed task.

await is roughly translated to checking whether the awaited task completed, if so continue on executing the rest of the method synchronously and if not add the rest as a continuation on that task.

private static readonly Task _completedTask = Task.FromResult(false);
public override async Task<IHttpActionResult> Delete(int id)
{
    await _completedTask;
    return ResponseMessage(Request.CreateResponse(HttpStatusCode.MethodNotAllowed, new NotSupportedException()));
}

In .Net 4.6 you can use the new Task.CompletedTask property instead of creating your own completed task.

This enables you to keep the method async and with it keep the same error-handling semantics.

Community
  • 1
  • 1
i3arnon
  • 113,022
  • 33
  • 324
  • 344
  • And then you create a new object of a hidden class, call methods on it that use that cached completed task, and then have that object garbage collected. I'm not talking about the task that the method is `await`ing, I'm talking about the task that it returns. Again, it's not a serious hit, but it's not true that it "doesn't degrade performance" either. – Jon Hanna May 28 '15 at 09:34
  • @JonHanna All options must return a new task. – i3arnon May 28 '15 at 09:37
  • @JonHanna but if you're refering to the state machine being generated by the async method, then it's being generated in the original code as well, so **there is no degradation** – i3arnon May 28 '15 at 09:38
  • @JonHanna moreover, that state machine is a struct, and only moves to the heap when you really suspend the method, which doesn't happen when the awaited task is completed. So **there's no allocation, no collection and no degradation**. – i3arnon May 28 '15 at 09:39
  • You're correct, there's no allocation. There is still more done than in Yuval's answer though, unless the methods involved get inlined. – Jon Hanna May 28 '15 at 09:41
  • @JonHanna again, there's no degradation over the *original code*. – i3arnon May 28 '15 at 09:42