首页 文章

有效地使用ASP.NET Web API的async / await

提问于
浏览
75

我试图在我的Web API项目中使用ASP.NET的 async/await 功能 . 我不确定它是否会对我的Web API服务的性能产生任何影响 . 请在下面找到我的应用程序中的工作流程和示例代码 .

Work Flow:

UI应用程序→Web API endpoints (控制器)→Web API服务层中的调用方法→调用另一个外部Web服务 . (这里我们有数据库交互等)

Controller:

public async Task<IHttpActionResult> GetCountries()
{
    var allCountrys = await CountryDataService.ReturnAllCountries();

    if (allCountrys.Success)
    {
        return Ok(allCountrys.Domain);
    }

    return InternalServerError();
}

Service Layer:

public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
{
    var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");

    return Task.FromResult(response);
}

我测试了上面的代码并且正在运行 . 但我不确定它是否正确用于 async/await . 请分享你的想法 .

4 回答

  • 143

    你没有有效地利用async / await,因为执行同步方法时会阻塞请求线程 ReturnAllCountries()

    分配用于处理请求的线程将暂时等待 ReturnAllCountries() 它的工作 .

    如果您可以实现 ReturnAllCountries() 是异步的,那么您会看到可伸缩性的好处 . 这是因为线程可以释放回.NET线程池来处理另一个请求,而 ReturnAllCountries() 正在执行 . 通过更有效地利用线程,这将使您的服务具有更高的吞吐量 .

  • 6

    我会将您的服务层更改为:

    public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
    {
        return Task.Run(() =>
        {
            return _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
        }      
    }
    

    正如你所拥有的那样,你仍在同步运行你的 _service.Process 呼叫,并且等待它几乎没有任何好处 .

    使用这种方法,您将在 Task 中包含潜在的慢速调用,启动它,并将其返回等待 . 现在你得到了等待 Task 的好处 .

  • -4

    我不确定它是否会对我的API性能产生任何影响 .

    请记住,服务器端异步代码的主要好处是可伸缩性 . 它不会神奇地使您的请求运行得更快 . 我在article on async ASP.NET中介绍了几个“我应该使用 async ”的注意事项 .

    我认为你的用例(调用其他API)非常适合异步代码,请记住"asynchronous"并不意味着"faster" . 最好的方法是首先使你的 UI 响应和异步;这将使您的应用程序感觉更快,即使它稍慢 .

    就代码而言,这不是异步的:

    public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
    {
      var response = _service.Process<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
      return Task.FromResult(response);
    }
    

    您需要一个真正的异步实现来获得 async 的可伸缩性优势:

    public async Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
    {
      return await _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
    }
    

    或者(如果你在这个方法中的逻辑真的只是一个传递):

    public Task<BackOfficeResponse<List<Country>>> ReturnAllCountriesAsync()
    {
      return _service.ProcessAsync<List<Country>>(BackOfficeEndpoint.CountryEndpoint, "returnCountries");
    }
    

    请注意,'s easier to work from the 929669 rather than the 929670 like this. In other words, don' t以异步控制器操作开始,然后强制下游方法异步 . 相反,确定自然异步操作(调用外部API,数据库查询等),并首先将这些异步设置为最低级别( Service.ProcessAsync ) . 然后让 async 涓涓细流,使控制器操作异步作为最后一步 .

    在任何情况下都不应该在这种情况下使用 Task.Run .

  • 0

    这是正确的,但可能没用 .

    由于没有什么可以等待 - 没有调用阻止可以异步操作的API - 然后你正在设置结构来跟踪异步操作(有开销),但后来没有使用该功能 .

    例如,如果服务层使用支持异步调用的Entity Framework执行数据库操作:

    public Task<BackOfficeResponse<List<Country>>> ReturnAllCountries()
    {
        using (db = myDBContext.Get()) {
          var list = await db.Countries.Where(condition).ToListAsync();
    
           return list;
        }
    }
    

    在查询数据库时,您将允许工作线程执行其他操作(从而能够处理另一个请求) .

    Await往往需要一直向下:很难适应现有系统 .

相关问题