C# 异步函数在WebMethod中会死锁,但在Page_Load中调用时效果良好

C# 异步函数在WebMethod中会死锁,但在Page_Load中调用时效果良好,c#,asynchronous,webforms,webmethod,C#,Asynchronous,Webforms,Webmethod,我有一个WebForms项目,它试图利用一些async代码。在早期开发过程中,我只是在页面加载中调用我的异步代码(这也是异步),并将结果写入响应。很好,易于测试。一切如期进行 代码可以总结为: 获取Azure授权令牌 向Azure的REST API发出HTTP请求 将结果返回给客户端 因此,当我试图在WebMethod中调用这个async方法时,我感到非常惊讶,但失败了。它似乎在其他async代码(特别是Azure AD方法)上处于死锁状态 我的第一个想法是一些上下文魔法,但是Configure

我有一个WebForms项目,它试图利用一些
async
代码。在早期开发过程中,我只是在
页面加载
中调用我的
异步
代码(这也是
异步
),并将结果写入
响应
。很好,易于测试。一切如期进行

代码可以总结为:

  • 获取Azure授权令牌
  • 向Azure的REST API发出HTTP请求
  • 将结果返回给客户端
  • 因此,当我试图在
    WebMethod
    中调用这个
    async
    方法时,我感到非常惊讶,但失败了。它似乎在其他
    async
    代码(特别是Azure AD方法)上处于死锁状态

    我的第一个想法是一些上下文魔法,但是
    ConfigureAwait(false)
    没有任何效果。所以我完全不知道有什么不同。我尝试过
    WebMethod
    async
    还是非
    的方法,结果没有差别。快速测试表明,这种
    WebMethod
    的配置方式没有任何问题——让我的代码尽早返回(在调用AD代码之前)可以让它按预期工作。尽管那时没有发生任何异步事件

    我还尝试使用非异步方法获取Azure AD令牌。这可以正常工作,但是当我的应用程序尝试发出HTTP请求时(使用RestSharp.Portable库,它只提供
    async
    方法),它就会卡住。我的代码如下所示:

    protected async Task<JObject> PerformRequest(string path, Method method, string apiVersion, string requestBody)
    {
        RestClient client = new RestClient("https://management.azure.com");
        RestRequest request = new RestRequest(path, method);
        request.AddParameter("api-version", apiVersion);
        request.AddHeader("Authorization", await this.GetAccessTokenAsync());
        IRestResponse<JObject> response = await client.Execute<JObject>(request);
        return response.Data;
    }
    
    public async Task<string> GetAccessTokenAsync()
    {
        string tenantId = "xxx";
        AuthenticationContext authenticationContext = new AuthenticationContext($"https://login.windows.net/{tenantId}");
        ClientCredential credential = new ClientCredential(clientId: "xxx", clientSecret: "xxx=");
        AuthenticationResult result = await authenticationContext.AcquireTokenAsync(resource: "https://management.core.windows.net/", clientCredential: credential);
    
        if (result == null) throw new InvalidOperationException("Failed to obtain the JWT token");
    
        return this.accessToken = result.AccessTokenType + " " + result.AccessToken;
    }
    
    对所有这些的调用都直接在
    WebMethod
    中进行(这是
    WebMethod
    目前唯一做的事情):

    为什么我的应用程序在
    WebMethod
    中调用此代码而不是在
    Page\u Load
    中调用此代码时会出现此问题?我能做些什么来解决这个问题?

    更改您的

    IRestResponse<JObject> response = await client.Execute<JObject>(request);
    
    IRestResponse response=wait client.Execute(请求);
    

    IRestResponse response=wait client.Execute(请求)。configurewait(false);
    
    这对你有帮助吗?
    问题是两个“wait”都在等待同时捕获相同的上下文,ConfigureWait阻止此操作(读取其文档)

    我们可以看到您在哪里调用
    PerformRequest
    ?您是否在任何地方等待调用
    PerformRequest
    的结果?是否可以将代码发布到此.GetAccessToken?另外,请您使用HTTP嗅探器(如fiddler)发布运行此代码的结果。@Aron,我已为
    GetAccessToken
    添加了代码。这与MDSN演示的内容几乎相同。@spender,我已将此添加到问题中。听起来您是在建议两个内部任务同时运行。但我认为它应该按顺序运行,不应该“同时”发生。请注意,异步/等待模式是一个陷阱是的。但正是与Task.Result的交互导致了问题。OP正在运行“一路异步”。不,这没有什么区别。我不明白为什么会这样,因为这两个等待应该是连续的(必须在发出请求之前设置身份验证头)。无论如何,试图改变不会导致任何变化。它似乎也没有解释为什么这会“正常”工作,但不适用于
    WebMethod
    IRestResponse<JObject> response = await client.Execute<JObject>(request);
    
    IRestResponse<JObject> response = await client.Execute<JObject>(request).ConfigureAwait(false);