2016-08-17 121 views
0

我想從Azure AD獲取訪問令牌,並且異步調用永遠不會結束。我不確定自己做錯了什麼,也許有人可以指引我走向正確的方向。這是代碼:從非異步方法運行異步代碼

private static void GetAccessTokenNonAsync() 
    { 
     Func<System.Threading.Tasks.Task> task = async() => { await GetAccessToken().ConfigureAwait(false); }; 
     task().Wait(); 
    } 
    private static async Task<AuthenticationResult> GetAccessToken() 
    { 
     string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"]; 
     string clientId = ConfigurationManager.AppSettings["ida:ClientId"]; 
     string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"]; 
     string source = ConfigurationManager.AppSettings["ExchangeOnlineId"]; 

     var authContext = new AuthenticationContext(aadInstance, false); 
     var credentials = new ClientCredential(clientId, clientSecret); 
     var appRedirectUrl = HttpContext.Current.GetOwinContext().Request.Scheme + "://" + HttpContext.Current.GetOwinContext().Request.Host + HttpContext.Current.GetOwinContext().Request.PathBase + "/"; 
     var res = 
      await 
       authContext.AcquireTokenByAuthorizationCodeAsync(
        ((ClaimsIdentity)HttpContext.Current.User.Identity).FindFirst("AuthenticationCode").Value, 
        new Uri(appRedirectUrl), credentials, source).ConfigureAwait(false); 
     Current.AccessToken = res.AccessToken; 
     return res; 

    } 
    private static string AccessToken 
    { 
     get 
     { 
      return HttpContext.Current.Session["AccessToken"].ToString(); 
     } 
     set { HttpContext.Current.Session["AccessToken"] = value; } 
    } 

而在我的主要方法我稱之爲GetAccessTokenNonAsync()

+2

的可能的複製[我將如何運行的異步任務方法同步?] (http://stackoverflow.com/questions/5095183/how-would-i-run-an-async-taskt-method-synchronously) – Igor

+1

@Igor:在這個問題上接受的答案有嚴重的重入問題。請不要推薦它。 –

回答

1

您使用async關鍵字不當。像這樣的東西應該工作:

public static void GetAccessTokenNonAsync() 
{ 
    // Call the async method and get the resulting task. 
    Task<AuthenticationResult> accessTokenTask = GetAccessToken(); 
    // Wait for the task to finish. 
    accessTokenTask.Wait(); 
} 

private async static Task<AuthenticationResult> GetAccessToken() 
{ 
    return await Task.Factory.StartNew<AuthenticationResult> 
    (
     () => 
     { 
      // Insert code here. 
      return new AuthenticationResult(); 
     } 
    ); 
} 
0

由於您的異步代碼返回的東西(使用任務簽名),嘗試,如果你的目標最新的.NET Framework這樣做:

private static void GetAccessTokenNonAsync() 
{ 
    var result = GetAccessToken().ConfigureAwait(false).Result; 
} 
3

您正在運行到deadlock scenario because you're blocking on async code 。我在我的博客上描述了細節。

理想情況下,您不應該在任何新代碼中使用HttpContext.Current。它被認爲是很差的練習,並且已經在ASP.NET Core中完全刪除(並且不會回來)。

它看起來像你想要做的是將令牌存儲在會話狀態,並(如果有必要,(異步))創建它。適當的方式做,這是使訪問異步:

public static async Task<string> GetAccessTokenAsync() 
{ 
    var result = HttpContext.Current.Session["AccessToken"] as string; 
    if (result != null) 
    return result; 

    ... 

    var res = await authContext.AcquireTokenByAuthorizationCodeAsync(...); 
    result = res.AccessToken.ToString(); 
    HttpContext.Current.Session["AccessToken"] = result; 
    return result; 
} 
+0

我不認爲這解決了我的問題。我需要在用戶需要使用它時重新生成訪問令牌,所以我需要它同步。我還在所有的異步調用上使用ConfigureAwait(false),但它仍然鎖定用戶界面... –

+0

@OanaMarina:這不是問題;你只是異步而非同步地重新生成它。 'ConfigureAwait(false)'只在整個調用堆棧中使用時纔有效; 'AcquireTokenByAuthorizationCodeAsync'可能沒有使用它。 –

+0

如果我異步重新生成它,我不能確定它會在我使用它的下一行代碼中可用。我需要等待它生成,然後使用它... –

0

終於工作的代碼是這樣的:

private static void GetAccessTokenNonAsync() 
    { 
     var userId = ((ClaimsIdentity)HttpContext.Current.User.Identity).FindFirst("UserId").Value; 

     var task = System.Threading.Tasks.Task.Run(async() => 
     { 
      return await GetAccessToken(userId); 
     }); 
     task.Wait(); 
     Current.AccessToken = task.Result.AccessToken; 
     Current.AccessTokenExpiresOn = task.Result.ExpiresOn.ToString(); 
    } 

    private static async Task<AuthenticationResult> GetAccessToken(string userId) 
    { 

     return await System.Threading.Tasks.Task.Factory.StartNew<AuthenticationResult> 
     (
      () => 
      { 
       string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"]; 
       string clientId = ConfigurationManager.AppSettings["ida:ClientId"]; 
       string clientSecret = ConfigurationManager.AppSettings["ida:ClientSecret"]; 
       string source = ConfigurationManager.AppSettings["ExchangeOnlineId"]; 

       var authContext = new AuthenticationContext(aadInstance, false); 
       var credentials = new ClientCredential(clientId, clientSecret); 
       var res = 
        authContext.AcquireTokenSilentAsync(source, credentials, 
         new UserIdentifier(userId, UserIdentifierType.UniqueId)).Result; 
       return res; 
      } 
     ); 
    }