【问题标题】:How do I get Async/Await working in ASP.NET MVC controller如何让 Async/Await 在 ASP.NET MVC 控制器中工作
【发布时间】:2020-11-25 04:07:06
【问题描述】:

考虑这个名为 Forward 的非异步方法,当使用 Forward(forwardUrl, content); 调用(在 C# 中)时,它的工作方式与预期完全一样

    private ActionResult Forward(string forwardUrl, FormUrlEncodedContent content)
    {
        // NOTE - httpClient is a static HttpClient instance

        HttpResponseMessage response;
        switch (Request.RequestType.ToUpperInvariant())
        {
            case "GET":
                response = httpClient.GetAsync(forwardUrl).GetAwaiter().GetResult();
                break;
            case "POST":
                response = httpClient.PostAsync(forwardUrl, content).GetAwaiter().GetResult();
                break;
            default:
                throw new InvalidOperationException($"Unable to forward Request method {Request.RequestType}.");
        }

        if (response.IsSuccessStatusCode)
        {
            return Content(response.Content.ReadAsStringAsync().GetAwaiter().GetResult(), response.Content.Headers.ContentType.ToString());
        }

        return new HttpStatusCodeResult(response.StatusCode, response.ReasonPhrase);
    }

那个工作正常,但反复调用GetAwaiter().GetResult() 基本上阻塞并使其同步。

现在我有以下称为ForwardAsync 的异步方法,在这里我使用完全相同的参数调用它,但使用ForwardAsync(forwardUrl, content).GetAwaiter().GetResult(); 的调用模式

    private async Task<ActionResult> ForwardAsync(string forwardUrl, FormUrlEncodedContent content)
    {
        HttpResponseMessage response;
        switch (Request.RequestType.ToUpperInvariant())
        {
            case "GET":
                response = await httpClient.GetAsync(forwardUrl);
                break;
            case "POST":
                response = await httpClient.PostAsync(forwardUrl, content);
                break;
            default:
                throw new InvalidOperationException($"Unable to forward Request method {Request.RequestType}.");
        }

        if (response.IsSuccessStatusCode)
        {
            return Content(await response.Content.ReadAsStringAsync(), response.Content.Headers.ContentType.ToString());
        }

        return new HttpStatusCodeResult(response.StatusCode, response.ReasonPhrase);
    }

这个只是挂了。在我的测试用例中,我将沿着POST 分支(在两种情况下),但代码永远不会从await httpClient.PostAsync(forwardUrl, content) 返回。我能够确认请求已发布,并且代码确实在转发的 URL 上运行并响应,但等待的方法调用根本不返回。

我做错了什么?

【问题讨论】:

  • 你能显示调用ForwardAsync的代码吗
  • 在描述中-return ForwardAsync(forwardUrl, content).GetAwaiter().GetResult();
  • 不要阻止 async 方法,期间。但是,如果您真的需要这样做(在正常情况下很少需要这样做),请牺牲一个线程池线程并卸载它,然后运行任务然后阻止它。
  • 您可能想要edit 帖子的标题,因为听起来您实际上并不想在控制器中正确使用 async/await,而是正确地从同步方法中调用异步方法(实际上不是一个好主意,但与您的问题无关)
  • 请澄清您是使用ForwardAsync(forwardUrl, content).GetAwaiter().GetResult()(在您的测试中)调用您的控制器端点还是使用HttpClient 进行真正的HTTP 调用?

标签: c# asp.net-mvc async-await


【解决方案1】:

那个工作正常,但反复调用 GetAwaiter().GetResult() 基本上阻塞并使其同步。

现在我有以下称为 ForwardAsync 的异步方法,在这里我使用完全相同的参数调用它,但调用模式为 ForwardAsync(forwardUrl, content).GetAwaiter().GetResult();

新方法也阻塞并且是同步的。我不确定进行此更改会获得什么价值。

另外,by blocking on asynchronous code, the new code is susceptible to deadlocks

我建议:

  • 保持现有代码不变。
  • 使新代码正确异步。这意味着使用await 调用它而不使用GetAwaiter().GetResult()

不要在 ASP.NET 上使用 Task.Run。在那种环境下,线程池属于 ASP.NET,经常使用Task.Run 会抛出线程池启发式。

【讨论】:

  • 谢谢@StephenCleary。我已经开始标记这个“答案”,但仍然存在一个悖论。我正在使用旧代码库。我正在添加新功能。 MSDN 强烈建议在开发新功能时不要使用旧的 WebRequest 同步代码。无论如何,我还需要 async/await,因为我正在使用一些需要它的较新的 SDK。因此,除非我能说服旧代码项目管理团队在整个过程中(痛苦)进行升级,否则我会被困在(旧的)ASP.NET 上下文中,使用.GetAwaiter().GetResult()Task.Run().Result
  • @StephanG:您可能会发现我的Brownfield Async 文章很有帮助。我的回答(和博客文章)鼓励了理想的解决方案,但在现实世界中,有时事情会变得一团糟。在您的情况下,听起来一直保持GetAwaiter().GetResult() 呼叫可能是最好的解决方案。我一定会提供一个异步 API(使用文章中的 Boolean Argument Hack),以便您的消费者在准备好后可以采用async
  • 谢谢@StephenCleary。不错的文章。
【解决方案2】:

简答 - 在这种情况下使用:

return Task.Run(() =&gt; ForwardAsync(forwardUrl, content)).Result;

并且不要使用 GetAwaiter().GetResult() 模式。

我发现这个 SO 问题很有帮助,而且答案更长更正确:HttpClient.GetAsync(...) never returns when using await/async

非常感谢所有对最初问题发表评论并阐明见解的人。

【讨论】:

  • 不要在 Web 应用程序上使用 Task.Run。它弊大于利。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-10-10
  • 2017-10-24
  • 1970-01-01
  • 2010-11-17
  • 1970-01-01
  • 1970-01-01
  • 2015-11-10
相关资源
最近更新 更多