Web API Service - How to use “HttpContext.Current” inside async task

廉价感情. 提交于 2019-12-04 03:25:52

From the beginning:

public async Task<object> Post([FromBody]string data)
{
  object response = ExecuteServerLogics(data);
  return response;
}

Don't ignore compiler warnings; the compiler will generate a warning for this method that specifically states it will run synchronously.

Moving on:

in some of the client's calls, we experienced performance issues.

Asynchronous code on the server will not be faster for a single call in isolation. It only helps you scale your server.

In particular, Task.Run will negate all the performance benefits of async and then degrade performance a bit beyond that. I believe the improvement in performance that you measured was coincidental.

in most posts we found that we should pass the worker thread's HttpContext reference into the inner Task that executes the server logics.

Those posts are wrong. IMHO. You end up using the HttpContext object from a background thread, when that object is specifically designed to be only accessed from a request thread.

am I doing the entire design wrong somehow?

I do recommend you take a step back and think about the big picture. When a request comes in, it has a certain amount of work to do. Whether that work is done synchronously or asynchronously is immaterial to the client; both approaches will take about the same amount of time.

If you need to return early to the client, then you'll need a completely different architecture. The usual approach is to queue the work to a reliable queue (e.g., Azure queue), have a separate backend (e.g., Azure WebRole), and proactively notify the client when the work is completed (e.g., SignalR).

That's not to say that async is useless, though. If ExecuteServerLogics is an I/O bound method, then it should be made asynchronous rather than blocking, and then you can use asynchronous methods as such:

public async Task<object> Post([FromBody]string data)
{
  object response = await ExecuteServerLogicsAsync(data);
  return response;
}

This will enable your server to be more responsive and scalable overall (i.e., not get overwhelmed by many requests).

If your task is inside your ApiController-derived class, you can use:

var ctx = this.Request.Properties["MS_HttpContext"] as System.Web.HttpContextWrapper;

This will give you an HttpContext wrapper with all the usual properties.

Amir I think you're looking for something like this below. I've been dealing with the same issue, trying to optimize a series of calls. It needs to be async all the way through, which means your ExecuteServerLogics() would need to be async, and you'd have to mark the containing lamda as async as well.

I believe following that pattern you can probably eliminate most of your performance issues. Nice passing the context through like that.

public async Task<object> Post([FromBody]string data)
{
      // Save worker context:
      var currentContext = HttpContext.Current; 

      object response = await Task<object>.Run(async () =>
      {
           // Set the context of the current task :
           HttpContext.Current = currentContext ;

           // Executes logics for current request:
           return await ExecuteServerLogics(data);
      });

      return response;
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!