First of all, I'm attempting to build a plugin for a product - so the environment is not mine and I don't have a ton of visibility into what's going on. As far as I can tell, the system is running .NET Framework 4.6.2, and I've tried to ensure all my dependencies match what's on the system.
The issue I'm seeing is that when I issue a await PostAsync(...)
request, I get a NullReferenceException
. This is 100% reproducible. However, if I make the exact same request using PostAsync(...).Result
, everything works properly.
This is made stranger given that I can use the await
keyword on the parsing of the response, and it still works correctly.
I want to do it the 'right' way, using the await
keyword, but it just doesn't work. From everything I've been reading, it's likely a dependency issue, but I don't know where to look to find it. I've spent 3 days on this already, when I finally discovered that using .Result
'fixes' my issue.
Relevant code:
private async Task<T> aFunction<T>(string path, Dictionary<string, string> data) {
var content = new FormUrlEncodedContent(data);
var response = client.PostAsync(path, content).Result; // This works
// var response = await client.PostAsync(path, content); // This does not
response.EnsureSuccessStatusCode();
var result = await response.Content.ReadAsStringAsync();
var parsed = JsonConvert.DeserializeObject<T>(result);
return parsed;
}
Stack Trace of the NRE:
Stack trace:
at System.Web.ThreadContext.AssociateWithCurrentThread(Boolean setImpersonationContext)
at System.Web.HttpApplication.OnThreadEnterPrivate(Boolean setImpersonationContext)
at System.Web.LegacyAspNetSynchronizationContext.CallCallbackPossiblyUnderLock(SendOrPostCallback callback, Object state)
at System.Web.LegacyAspNetSynchronizationContext.CallCallback(SendOrPostCallback callback, Object state)
at System.Threading.Tasks.AwaitTaskContinuation.RunCallback(ContextCallback callback, Object state, Task& currentTask)
--- End of stack trace from previous location where exception was thrown ---
at System.Threading.Tasks.AwaitTaskContinuation.<>c.<ThrowAsyncIfNecessary>b__18_0(Object s)
at System.Threading.ExecutionContext.RunInternal(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean preserveSyncCtx)
at System.Threading.QueueUserWorkItemCallback.System.Threading.IThreadPoolWorkItem.ExecuteWorkItem()
at System.Threading.ThreadPoolWorkQueue.Dispatch()
Since the ASPX page that was hosting the plugin is not async, I could not use async methods inside my handler. I ended up having to ensure that I use synchronous calls instead of the async ones.
To prove this, after adding the line (recommended by Martin)
<add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
and using the await calls, my plugin started to hang after making the request.
This SO page: Async Deadlock?, implied to me that the page on which my plugin was sitting was not properly setup for async functionality. I tried adding the RegisterAsyncTask, and in the logs I see a new message:
InvalidOperationException: This operation requires the page to be asynchronous (the Async attribute must be set to true).