C# await client.GetAsync() failing - c#

I'm pulling data from Amazon via HTTP. The code works just fine in a small demo project, but in my main app it doesn't. When I call FetchItem() I receive this output:
'System.Net.Http.Formatting.dll'. Cannot find or open the PDB file.
After await client.GetAsync() the function returns and url.Wait() waits forever.
Usage
Task<string> url = FetchItem("ItemName", requestUrl);
url.Wait();
return url.Result;
Source of FetchItem
private static async Task<string> FetchItem(string sItemName, string url)
{
try
{
HttpClient client = new HttpClient();
HttpResponseMessage response = await client.GetAsync(url);
response.EnsureSuccessStatusCode();
XElement content = await response.Content.ReadAsAsync<XElement>();
XNamespace ns = NAMESPACE;
var isValidResults = content.Descendants(ns + "IsValid").AsParallel();
foreach (var item in isValidResults)
{
if (item.Value != "True")
return "Invalid Request";
}
var titleResults = content.Descendants(ns + sItemName).AsParallel();
foreach (var item in titleResults)
{
if (item.Name == ns + sItemName)
return item.Value;
// depending on the keyword, results can be quite fun.... :-)
}
}
catch (Exception e)
{
System.Console.WriteLine("Caught Exception: " + e.Message);
System.Console.WriteLine("Stack Trace: " + e.StackTrace);
}
return "Error";
}

I assume you're calling this code on the UI thread. What's happening is that Waiting for the task to complete causes a deadlock. Your FetchItem method is asynchronous, and when you use await in it, the code that follows is transformed to a callback (called the continuation) that will be executed on the UI thread. But since the UI thread is busy waiting for the task to complete, it can't process the callback, so the task never completes. Hence the deadlock.
You should never Wait on the result of an async method. If you call an async method, use await to get its result. It means that the calling method also has to be async. Basically, when you start to use async in some part of the code, all the code that uses it needs to become async as well... (*)
(*) well, that's not exactly true. You can prevent the async method from resuming on the UI thread, by calling ConfigureAwait(false) on the method you await. If you do that, the continuation will run on a thread pool thread, instead of the UI thread. This will also avoid the deadlock.

I changed FetchItem() to run sync. That did it for me:
private static string GetItem(string sItemName, string url)
{
try
{
HttpClient client = new HttpClient();
HttpResponseMessage response = client.GetAsync(url).Result;
response.EnsureSuccessStatusCode();
XElement content = response.Content.ReadAsAsync<XElement>().Result;
...
It doesn't make sense to me to rewrite half my app to go async.

Related

WebAPI HTTP request not completing until queued work kicks off on background task

In my .Net 6 WebPI service, I am queueing work to a background task queue, very closely based on the example here, but I could post parts of my implementation if that would help:
https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-6.0&tabs=visual-studio#queued-background-tasks
I am running into unexpected behavior where control is not returned to the caller, even after the return Ok(..) completes in the controller. Instead the request only completes after the await Task.Delay(1000); line is reached on the queued work item. The request returns to the client as soon as this line is reached, and does not need to wait for the Delay to finish.
I'm guessing this is because of the await either starting a new async context, or someone un-sticking the async context of the original request. My intention is for the request to complete immediately after queuing the work item.
Any insight into what is happening here would be greatly appreciated.
Controller:
public async Task<ActionResult> Test()
{
var resultMessage = await _testQueue.DoIt();
return Ok(new { Message = resultMessage });
}
Queueing Work:
public TestAdapter(ITaskQueue taskQueue)
{
_taskQueue = taskQueue;
}
public async Task<string> DoIt()
{
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) =>
{
await Task.Delay(1000);
var y = 12;
});
return "cool";
}
IoC:
services.AddHostedService<QueueHostedService>();
services.AddSingleton<ITTaskQueue>(ctx =>
{
return new TaskQueue(MaxQueueCount);
});
TaskQueue:
private readonly Channel<BackgroundTaskContext> _queue;
public TaskQueue(int capacity)
{
var options = new BoundedChannelOptions(capacity)
{
FullMode = BoundedChannelFullMode.Wait
};
_queue = Channel.CreateBounded<BackgroundTaskContext>(options);
}
public async ValueTask QueueBackgroundWorkItemAsync(
Func<CancellationToken, ValueTask> workItem)
{
if (workItem == null)
{
throw new ArgumentNullException(nameof(workItem));
}
await _queue.Writer.WriteAsync(new BackgroundTaskContext(workItem, ...));
}
Not sure what you expect here. I'm assuming you want the async method to return the cool in the api response. That's fine but because your also awaiting the async call with in DoIt(), then it pauses until QueueBackgroundWorkItemAsync finishes. You could remove the await and it will run and return as you expect.
I can't say that I'm a big fan of that design as you lose contact with it with exception of the cancellation token. Another thought would be to Send that work off to a console job or function app using message bus or even another http call.
Additional Notes:
Async can be complicated to explain because in reality, it wraps up code and executes on a thread of it's choosing. The await simulates the synchronous behavior.
await Task.Delay(1000); // Runs on it's own thread but still halts code execution for 1 second.
await _taskQueue.QueueBackgroundWorkItemAsync(async (_cancellationToken) // Waits for control to be returned from the code inside.
var resultMessage = await _testQueue.DoIt(); // Always waits for the code inside to complete.
If your wanting something to run without pausing code execution, you can either remove the await or add a Task.Run(() => { }); pattern. Is it a good idea is a whole other question. It also matters whether you need information back from the async method. If you don't await it then you'll get null back as it doesn't wait around for the answer to be computed.
This appears just to have been user error using the debugger. The debugger is switching to the background task thread and hitting breakpoints there before the response fully returns giving the appearance that control was not being returned to the client and being carried into the background task.
Even after adding some synchronous steps in QueueBackgroundWorkItemAsync and putting breakpoints on them, control was not immediately returned to the original http call. Only after I tried adding a long running task like await Task.Delay(1000); did enough time/ticks pass for the http response to return. I had conflated this with just the await somehow freeing up the original http context.

async method not run asynchronously

I am writing below code in WPF. However, it does not run asynchronously. I have commented the line where the code gets blocked. Please let me know where am I doing the mistake.
private async void txtSampleRequest_Click(object sender, RoutedEventArgs e)
{
await DownloadData();
}
async Task<string> DownloadData()
{
string authorization = txtAuthentication.Text;
string cookie = txtCookie.Text;
try
{
var vvv = Enumerable.Range(0, 50);
List<Task> TaskList = new List<Task>();
foreach (int s in vvv)
{
Task LastTask = ZerodhaOperations.MyHttpRequest("", authorization, cookie, "5minute");//Even this method is async method, and I have added dummy Task.Delay(1) to run method asynchronously. Below is the full definition of this class.
TaskList.Add(LastTask);
}
await Task.WhenAll(TaskList); //<------------ Here it stops working asynchronously and blocks UI of application.
MessageBox.Show("DONE");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
return "";
}
class ZerodhaOperations
{
public static async Task<string> MyHttpRequest(string WEBSERVICE_URL,string authorization, string cookie,string timeFrame)
{
await Task.Delay(1);
if (authorization == "" || cookie == "" || authorization == null || cookie == null)
{
throw new ArgumentException("Aditya Says, No Authorization and cookie has been set");
}
string jsonResponse = "";
WEBSERVICE_URL = "https://kite.zerodha.com/oms/instruments/historical/519937/timeFrame?user_id=YD0744&oi=1&from=2020-04-24&to=2020-04-24";
WEBSERVICE_URL = $"https://kite.zerodha.com/oms/instruments/historical/806401/{timeFrame}?user_id=YD0744&oi=1&from=2020-12-03&to=2020-12-03";
try
{
var webRequest = System.Net.WebRequest.Create(WEBSERVICE_URL);
//ServicePointManager.SecurityProtocol = SecurityProtocolType.Ssl3;
ServicePointManager.Expect100Continue = true;
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls
| SecurityProtocolType.Tls11
| SecurityProtocolType.Tls12
| SecurityProtocolType.Ssl3;
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
if (webRequest != null)
{
webRequest.Method = "GET";
webRequest.Timeout = 5000;
if (authorization != "")
{
webRequest.Headers.Add("authority", "kite.zerodha.com");
webRequest.Headers.Add("authorization", authorization);
}
webRequest.Headers.Add("cookie", cookie);
webRequest.Headers.Add("method", "GET");
using (System.IO.Stream s = webRequest.GetResponse().GetResponseStream())
{
using (System.IO.StreamReader sr = new System.IO.StreamReader(s))
{
jsonResponse = sr.ReadToEnd();
if (jsonResponse == "")
throw new Exception("Empty Response");
//MessageBox.Show(jsonResponse);
}
}
}
}
catch (Exception ex)
{
if (ex.Message.Contains("Empty Response"))
throw ex;
else
MessageBox.Show(ex.ToString());
}
return jsonResponse;
}
}
The way await works is that it captures a "context" when it asynchronously yields. The details are in the link, but in this case the asynchronous method is running on the WPF UI thread, so it captures a context that will resume running on that UI thread.
So the await Task.Delay(1) is in fact forcing MyHttpRequest to act asynchronously, but it's what happens next that is tripping you up. MyHttpRequest does the await, which captures the UI context and returns an incomplete task. This happens multiple times in DownloadData, which collects the incomplete tasks and then awaits the Task.WhenAll of them. So then DownloadData returns an incomplete task which is awaited by the event handler, which returns control back to the WPF message loop.
Now, what happens next is that those Task.Delay timers fire off almost immediately. So, MyHttpRequest resumes its async method, and since its await captured that UI context, it resumes running on the UI thread. So the await Task.Delay(1) did cause a yield to the WPF message loop, but then practically the very next thing the UI thread has to do is resume those async methods.
And the remainder of those async methods are synchronous, blocking the UI thread.
So, to solve this, you can make the methods truly asynchronous. Delete the await Task.Delay(1) completely and instead replace the synchronous APIs with asynchronous ones (GetResponseAsync, ReadToEndAsync). Or if you want to modernize the code further, replace WebRequest with HttpClient.
Your other option is to keep the code synchronous and just use Task.Run to run the synchronous code on background threads. Again, you would delete the await Task.Delay(1), and this time you would change the method signature to be synchronous. Then you can wrap the call to the (synchronous) MyHttpRequest in a Task.Run, e.g., Task LastTask = Task.Run(() => ZerodhaOperations.MyHttpRequest("", authorization, cookie, "5minute")); The Task.Run solution is less ideal than the asynchronous solution, but it is acceptable if you have a lot of other code that still needs MyHttpRequest to be synchronous for now.
Note that either way, the usage of Task.Delay(1) is wrong. It does not "run [the] method asynchronously". The proper way to run a method asynchronously is to have it call asynchronous APIs and not call blocking APIs.

Method with await GetAsync API call exits without a returning a result or running all code in the method

Borrowing from the Get method in https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client I have written the following. While being new to C# I assumed the await would complete the API call then move on, but what's happening is that line executes then exits the method without running another line of code (the if statement, the return). I've probably assumed other things as well, which has led me to this predicament.
What should the code be to consistently get a response from the API call and deserialize the Json so I can retrieve the 2 bits of data I actually need?
Any other suggested improvements to my code also greatly appreciated.
The method call:
var ReturnedData = GetProductAsync(companyCode);
The method:
//API Call method - https://learn.microsoft.com/en-us/aspnet/web-api/overview/advanced/calling-a-web-api-from-a-net-client
static HttpClient client = new HttpClient();
static async Task<ReturnedAPIdata> GetProductAsync(string CoCode)
{
// TODO - This key will need to be taken out of the code and put in a config file or something
string DavesKey = "abc123";
string path = "http://api.marketstack.com/v1/eod?access_key=" + DavesKey + "&symbols=" + CoCode + ".XASX&sort=DESC&limit=1&offset=0";
ReturnedAPIdata ThatAPIdata = null;
HttpResponseMessage response = await client.GetAsync(path);
response.EnsureSuccessStatusCode();
if (response.IsSuccessStatusCode)
{
// If we have an API response, deserialise the Json data... somehow
//ThatAPIdata = await response.Content.ReadAsAsync<ReturnedAPIdata>(); // ReadAsAsync has been deprecated, but not replaced. Handy
string responseBody = await response.Content.ReadAsStringAsync();
// Which one of these fuckers actually works. Use an API, they said. It'll be fun, they said. ARG!
ReturnedAPIdata Arses = System.Text.Json.JsonSerializer.Deserialize<ReturnedAPIdata>(responseBody); // attempt 3
List<ReturnedAPIdata> Cock = JsonConvert.DeserializeObject<List<ReturnedAPIdata>>(responseBody); // attempt 2
ReturnedAPIdata Balls = JsonConvert.DeserializeObject<ReturnedAPIdata>(responseBody); // attempt 1
}
Console.WriteLine("FFFFFFFFFfffff....");
return ThatAPIdata;
}
The Conrole.Writeline at the end is just for a breakpoint so I can assess which deserialise attempt works best, I've yet to see be reached to stop on.
If the following code is run:
static void Main(string[] args)
{
Console.WriteLine("Before calling async method");
GetProductAsync();
Console.WriteLine("After calling async method");
}
static async Task<string> GetProductAsync()
{
await Task.Delay(5000);
Console.WriteLine("Inside async method after await");
return "Got all Products";
}
The following result is printed to console:
Before calling async method
System.Threading.Tasks.Task`1[System.String]
After calling async method
So the execution falls through even though the method GetProductAsync is defined as async and there's an await for 5 seconds, also notice that inside of the async method there's a Console.WriteLine("Inside async method after await"); which also doesn't get printed! And the return value doesn't get printed either!
So what's happening here? Just because the method was defined as async and there's an await inside of it won't mean the caller is guaranteed async execution!
Specifically the Main method calling it doesn't await the result so that's why it's falling through.
Now if the code is changed and Main is awaiting the GetProductAsync method:
static async Task Main(string[] args)
{
Console.WriteLine("Before calling async method");
var result = await GetProductAsync();
Console.WriteLine("After calling async method");
Console.WriteLine(result);
}
static async Task<string> GetProductAsync()
{
await Task.Delay(5000);
Console.WriteLine("Inside async method after await");
return "Got all Products";
}
Now the result printed to the console is:
Before calling async method
Inside async method after await
After calling async method
Got all Products
So in this case the result was awaited correctly inside of main, the execution returned to the caller and is now free to continue doing other tasks such as updating the GUI (which isn't the case in this simple console app).
Hopefully this illustrates what's happening here, basically the caller, in this case Main, also needs to await the result from the async GetProductAsync method to see the result.
This article does a good job of explaining various pitfalls and gotchas! without going in too deep
https://www.pluralsight.com/guides/understand-control-flow-async-await

Getting deadlock with foreach and await

I am trying to call a service, but the service has a max length per request, so I am splitting my request into multiple smaller requests.
I am then trying to to use HttpClient together with Await as its async
public async Task<string> CallGenoskanAsync(List<string> requestList)
{
ServicePointManager.ServerCertificateValidationCallback = delegate { return true; };
var credentials = new NetworkCredential(userId, password);
var tasks = new List<Task<string>>();
foreach (string requestString in requestList)
{
using (HttpClientHandler handler = new HttpClientHandler { Credentials = credentials })
{
using (HttpClient client = new HttpClient(handler))
{
client.BaseAddress = new Uri(baseAddress);
using (HttpResponseMessage response = client.GetAsync(requestString).Result)
using (HttpContent content = response.Content)
{
tasks.Add(content.ReadAsStringAsync());
}
}
}
}
Task.WaitAll(tasks.ToArray());
var result = "";
foreach (var task in tasks)
{
result += task.Result;
}
return result;
}
This code deadlocks when await client.GetAsync is called, it never finishes.
If I change that line to
using (HttpResponseMessage response = client.GetAsync(requestString).Result)
Then I dont get any deadlocks, I suppose I am using await improperly together with foreach, but I can not figure out how
Edit: Changed example code
The compiler will give you a warning for the code you posted; in particular, it will point out that CallGenoskanAsync is synchronous, not asynchronous.
The core problem is this line:
Task.WaitAll(tasks.ToArray());
When you're writing asynchronous code, you shouldn't block on it. To do so can cause deadlocks, as I explain in my blog post. The deadlock occurs because await will capture a "context" that it uses to resume execution of the async method. This "context" is SynchronizationContext.Current or TaskScheduler.Current, and many contexts (UI and ASP.NET request contexts in particular) only allow one thread. So, when your code blocks a thread (Task.WaitAll), it's blocking a thread in that context, and this prevents the await from continuing since it's waiting for that context.
To fix, make your code asynchronous all the way. As I explain in my async intro post, the asynchronous equivalent of Task.WaitAll is await Task.WhenAll:
await Task.WhenAll(tasks);
WhenAll also has the nice property that it unwraps the results for you, so you don't have to use the problematic Result property:
var results = await Task.WhenAll(tasks);
return string.Join("", results);
Relevant context is not provided:
If
Your code sample is part of a callstack that returns a Task
There is a blocking wait at the root of the callstack (e.g.,
DoSomething().Result or DoSomething.Wait())
You have a SynchronizationContext that's thread affinitized (IE, this is running pretty much anywhere but a Console application)
Then
Your synchronization context's thread may be blocked on the root Result/Wait(), so it can never process the continuation that the completed call to GetAsync() scheduled to it. Deadlock.
If you meet the above conditions, try applying .ConfigureAwait(false) to your awaited GetAsync. This will let the continuation get scheduled to a Threadpool thread. Be aware that you are potentially getting scheduled to a different thread at that point.

How to create HttpWebRequest without interrupting async/await?

I have a bunch of slow functions that are essentially this:
private async Task<List<string>> DownloadSomething()
{
var request = System.Net.WebRequest.Create("https://valid.url");
...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
This works nicely and asynchronously except for the call to WebRequest.Create - this single line freezes the UI thread for several seconds which sort of ruins the purpose of async/await.
I already have this code written using BackgroundWorkers, which works perfectly and never freezes the UI.
Still, what is the correct, idiomatic way to create a web request with respect to async/await? Or maybe there is another class that should be used?
I've seen this nice answer about asyncifying a WebRequest, but even there the object itself is created synchronously.
Interestingly, I'm not seeing a blocking delay with WebRequest.Create or HttpClient.PostAsync. It might be something to do with DNS resolution or proxy configuration, although I'd expect these operations to be implemented internally as asynchronous, too.
Anyway, as a workaround you can start the request on a pool thread, although this is not something I'd normally do:
private async Task<List<string>> DownloadSomething()
{
var request = await Task.Run(() => {
// WebRequest.Create freezes??
return System.Net.WebRequest.Create("https://valid.url");
});
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
//read stream and return data
}
}
That would keep the UI responsive, but it might be difficult to cancel it if user wants to stop the operation. That's because you need to already have a WebRequest instance to be able to call Abort on it.
Using HttpClient, cancellation would be possible, something like this:
private async Task<List<string>> DownloadSomething(CancellationToken token)
{
var httpClient = new HttpClient();
var response = await Task.Run(async () => {
return await httpClient.PostAsync("https://valid.url", token);
}, token);
// ...
}
With HttpClient, you can also register a httpClient.CancelPendingRequests() callback on the cancellation token, like this.
[UPDATE] Based on the comments: in your original case (before introducing Task.Run) you probably did not need the IProgress<I> pattern. As long as DownloadSomething() was called on the UI thread, every execution step after each await inside DownloadSomething would be resumed on the same UI thread, so you could just update the UI directly in between awaits.
Now, to run the whole DownloadSomething() via Task.Run on a pool thread, you would have to pass an instance of IProgress<I> into it, e.g.:
private async Task<List<string>> DownloadSomething(
string url,
IProgress<int> progress,
CancellationToken token)
{
var request = System.Net.WebRequest.Create(url);
// ...
using (var ss = await request.GetRequestStreamAsync())
{
await ss.WriteAsync(...);
}
using (var rr = await request.GetResponseAsync())
using (var ss = rr.GetResponseStream())
{
// read stream and return data
progress.Report(...); // report progress
}
}
// ...
// Calling DownloadSomething from the UI thread via Task.Run:
var progressIndicator = new Progress<int>(ReportProgress);
var cts = new CancellationTokenSource(30000); // cancel in 30s (optional)
var url = "https://valid.url";
var result = await Task.Run(() =>
DownloadSomething(url, progressIndicator, cts.Token), cts.Token);
// the "result" type is deduced to "List<string>" by the compiler
Note, because DownloadSomething is an async method itself, it is now run as a nested task, which Task.Run transparently unwraps for you. More on this: Task.Run vs Task.Factory.StartNew.
Also check out: Enabling Progress and Cancellation in Async APIs.
I think you need to use HttpClient.GetAsync() which returns a task from an HTTP request.
http://msdn.microsoft.com/en-us/library/hh158912(v=vs.110).aspx
It may depend a bit on what you want to return, but the HttpClient has a whole bunch of async methods for requests.

Categories