从非异步方法运行异步代码

本文关键字:异步 代码 运行 异步方法 | 更新日期: 2023-09-27 18:02:54

我正在尝试从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()

从非异步方法运行异步代码

您正在运行死锁场景,因为您正在阻塞异步代码。我在我的博客上描述了细节。

理想情况下,您不应该在任何新代码中使用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;
}

最终成功的代码是:

 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;
            }
        );
    }

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();
        }
    );
}

既然你的异步代码返回一些东西(使用任务签名),尝试这样做,如果你的目标是最新的。net框架:

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