I have this code to call an API which returns a token. However, it will only return if I replace this line:
var response = await TokenClient.PostAsync(EnvironmentHelper.TokenUrl, formContent);
with this line:
var response = TokenClient.PostAsync(EnvironmentHelper.TokenUrl, formContent).Result;
Why?
public static async Task<Token> GetAPIToken()
{
if (DateTime.Now < Token.Expiry)
return Token;
TokenClient.DefaultRequestHeaders.Clear();
TokenClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
TokenClient.BaseAddress = new Uri(EnvironmentHelper.BaseUrl);
TokenClient.Timeout = TimeSpan.FromSeconds(3);
var formContent = new FormUrlEncodedContent(new[]
{
new KeyValuePair<string, string>("grant_type", "client_credentials"),
new KeyValuePair<string, string>("client_id", EnvironmentHelper.APITokenClientId),
new KeyValuePair<string, string>("client_secret", EnvironmentHelper.APITokenClientSecret)
});
try
{
// HANGS - this next line will only ever return if suffixed with '.Result'
var response = await TokenClient.PostAsync(EnvironmentHelper.TokenUrl, formContent);
var content = await response.Content.ReadAsStringAsync();
dynamic jsonContent = JsonConvert.DeserializeObject(content);
Token token = new Token();
token.AccessToken = jsonContent.access_token;
token.Type = jsonContent.token_type;
token.Expiry = DateTimeOffset.Now.AddSeconds(Convert.ToDouble(jsonContent.expires_in.ToString()) - 30);
token.Scope = Convert.ToString(jsonContent.scope).Split(' ');
Token = token;
}
catch (Exception ex)
{
var m = ex.Message;
}
return Token;
}
The top-most code where awaiting is not performed has this: var wm = new WebRepo().GetWeb(Id).Result
You're running into a deadlock since the synchronous code is blocking on async code. The best solution is to remove the blocking and go async all the way.
The calling code is a public static property with a getter, referenced in over 100 places. I'm not sure how best to convert the property to async friendly
There are a few approaches to async properties, depending on what the property's semantics are. If it re-evaluates each time it's called, then it's really a method in disguise and should become an async Task<T>
method. If it is set once, then AsyncLazy<T>
may be a better choice.
Is there a way to wrap one of the calls? It is a mammoth task to go and refactor that much code.
There's no wrapping that works for all scenarios. However, there are some techniques for mixing sync and async code that can work for specific scenarios.
Here's what I would consider, in order of precedence:
GetAPIToken
(and its callers) to be sync instead of async. (Requires all calling code to be made synchronous, which may not be possible).GetAPIToken
callable both synchronously and asynchronously. (Requires the ability to change GetAPIToken
).GetAPIToken
asynchronously on a thread pool thread and sycnhronously block another thread on it.