I'm basing myself on this sample:
async-example-with-HttpClient
I have the exact same code, which I'm running from a test:
public async Task<IEnumerable<Stuff>> GetStuff()
{
var r = await DownloadPage("http://stackoverflow.com");
}
static async Task<string> DownloadPage(string url)
{
using (var client = new HttpClient())
{
using (var r = await client.GetAsync(new Uri(url)))
{
string result = await r.Content.ReadAsStringAsync();
return result;
}
}
}
But it doesn't work. The debugger just stops on the await client.GetAsync(new Uri(url)) call.
There's no exception being thrown, there's nothing in the Visual Studio output window.
I have Fiddler open with all sessions cleared, I see nothing changing on doing that GET call.
I have nothing to go on why this doesn't work.
Any ideas on why this doesn't work and what I can do?
Update
This does partly work when called from an API, not from the xunit test I was starting with. But, when called from the API the response from the backend isn't received and the caller keeps waiting forever:
If I keep the code entirely on the API side, so no calls to any deeper dependencies, everything works. Context switching must be causing issues somehow.
Related
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.
I've a web API on server side as below. It is using async-await pattern. All these methods are present inside MyClassController class
[Route("install")]
public async Task<IHttpActionResult> InstallProduct()
{
Log("milestone1");
Install();
Log("milestone6");
}
private bool Install()
{
MyClass.Register();
Log("milestone5");
}
private static async void Register()
{
Log("milestone2");
using (HttpClientHandler handler = new HttpClientHandler())
{
using (var client = new HttpClient(handler))
{
var method = "http://BaseUrlOfWebsite#2/api/applications";
using (MultipartFormDataContent form = new MultipartFormDataContent())
{
//make POST API call into website # 2
var response = await client.PostAsync(method, form);
Log("milestone3");
}
}
}
Log("milestone4");
}
Overall call is like this:
Client code -> install API on website # 1 -> applications API on website # 2
My expected log statements are in below order:
milestone1
milestone2
milestone3
milestone4
milestone5
milestone6
Actual log order that I see in log files is as below:
milestone1
milestone2
milestone5
milestone6
milestone3
milestone4
Code doesn't causes error anywhere but it returns 500 internal server error code to calling client. I've two questions here:
Why the logs are not as per my expectation? My understanding is that Register method should get blocked while calling external website as I'm using await while waiting for response.
Can this out of order code execution cause 500 internal server error even though my code isn't throwing any error?
Note: Same logs get printed in expected order when I debug the install API with debugger attached in Visual Studio.
Update: If I make the call to external website client.PostAsync run synchronously forcefully using Task.Run then I get 200 OK response. Why web API results in error if I can fire and forget the call to external website and my remaining code can run synchronously to finish the web API call.
Register method is async, it returns void, instead of Task, which could be awaited.
And instead of awaiting for task your code is running synchronously.
[Route("install")]
public async Task<IHttpActionResult> InstallProduct()
{
Log("milestone1");
await Install();
Log("milestone6");
}
private async Task Install()
{
await MyClass.Register();
Log("milestone5");
}
private static async Task Register()
{
Log("milestone2");
using (var handler = new HttpClientHandler())
{
using (var client = new HttpClient(handler))
{
var method = "http://BaseUrlOfWebsite#2/api/applications";
using (MultipartFormDataContent form = new MultipartFormDataContent())
{
//make POST API call into website # 2
var response = await client.PostAsync(method, form);
Log("milestone3");
}
}
}
Log("milestone4");
}
In general, when you see async void in your code it’s bad news, because:
you can’t wait for its completion (as mentioned in this post already)
any unhandled exceptions will terminate your process (ouch!)
Suppose you have a timer event that fires every once in a while and you want to do some asynchronous processing inside the handler, so you proceed to write something along the lines of:
var timer = new Timer();
timer.Elapse += OnTimerFired;
timer.Interval = 1000;
timer.Start();
...
private static async void Register()
{
await Task.Delay(1000);
// this is going to terminate your process!
throw new Exception();
}
I have a client server app. Wpf - Web api I am having a bit of troubles as this is working on one machine but when I switch to another machine it doesn't.
public async Task<IEnumerable<T>> Get()
{
var result = (IEnumerable<T>)null;
//Gets to this line and does not throw exception
var response = await _httpClient.GetAsync(BaseApiAddress);
if (!response.IsSuccessStatusCode)
{
ThrowInvalidException(response);
}
var resultAsString = await response.Content.ReadAsStringAsync();
result = JsonConvert.DeserializeObject<IEnumerable<T>>(resultAsString);
return result;
}
API method:
[HttpGet]
public virtual IEnumerable<T> Get()
{
var items = Repository.Get().ToArray();
return items;
}
and Repository:
public IQueryable<T> Get()
{
return _context.Set<T>();
}
I get to the api and it gets the data while debugging through the code it has the data and looks as if there it completes the method on the api. all of that said it never returns to the GetAsync method.
I have hit a brick wall here and really don't even know where to start looking from here. Any Suggestions would be very helpful.
If you run under a synchronization context, you'll get a deadlock if:
The caller waits synchronously on a task (for example, calling task.Result
Inside of the task, you use await
The fix is to use .ConfigureAwait(false) to avoid posting the continuation on the synchronization context:
var response = await _httpClient.GetAsync(BaseApiAddress).ConfigureAwait(false);
Of course, as much as possible, you should avoid waiting synchronously on a task.
I was reading the following topic http://blog.stephencleary.com/2012/07/dont-block-on-async-code.html
and decided to write a common utility method in my library to do a GET on remote url via HTTPClient
public static async Task<T> GetAsync<T>(HttpGetObject getObject)
{
string baseUrl = getObject.BaseUrl;
string actionUrl = getObject.ActionRelativeUrl;
string acceptType = getObject.AcceptType;
using (var client = new HttpClient())
{
client.BaseAddress = new Uri(baseUrl);
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue(acceptType));
AddCustomHeadersToHttpClient(client, getObject);
// HTTP GET
HttpResponseMessage httpResponseMessage = await client.GetAsync(actionUrl).ConfigureAwait(false);
if (httpResponseMessage.IsSuccessStatusCode)
{
T response = await httpResponseMessage.Content.ReadAsAsync<T>().ConfigureAwait(false);
return response;
}
else
{
string message = httpResponseMessage.Content.ReadAsStringAsync().Result;
throw new Exception(message);
}
}
return default(T);
}
I know the "await httpResponseMessage.Content.ReadAsAsync().ConfigureAwait(false)" will prevent the deadlock in the above code
First:
My query is for "string message = httpResponseMessage.Content.ReadAsStringAsync().Result" line, will .Result can cause deadlock or not in that line?
Second:
If I call that code from UI like this:
public static object DoGet()
{
// Build getObject
var task = Utility.GetAsync(getObject);
task.Wait();
var response = task.Result;
return response;
}
Will that cause a deadlock?
Please note that I know to avoid all the mess with async-await, all the methods from UI to DAL must be async-await but I am not in position at this moment to change all that structure, my goal at this moment is to call HttpClient library and do a few GET operations.
So my questions is that will the above code can cause a deadlock?
Third:
Is task.Wait(); even needed in the above code?
In the general case, you should assume that yes, calling .Result or .Wait() on anything awaitable is dangerous and can deadlock (unless you are the library issuing the task, and you understand the full context). It is possible that it will work OK in some specific cases, but you should not rely on that behaviour, even if it works today.
I have an identical method in two of my WP8 apps. Given the same url and same device, the method works on one app, but not the other. In the failing app, GetAsync is hanging after it is called. No time out, no exception.
Here is the method in question.
private async Task<Byte[]> DownloadData(string uri)
{
byte[] myDataBuffer = null;
var newUri = new Uri(uri, UriKind.Absolute);
var myWebClient = new HttpClient();
var response = await myWebClient.GetAsync(newUri);
if (response.Content.Headers.ContentType.MediaType == "text/plain"
|| response.Content.Headers.ContentLength < 200)
{
throw new Exception(await response.Content.ReadAsStringAsync());
}
myDataBuffer = await response.Content.ReadAsByteArrayAsync();
return myDataBuffer;
}
This happens every time on one particular app, but not the other. Same device. Has anybody ever experienced this behavior? The url is valid, the code is identical. Is there a project setting somewhere that might affect this? I'm using the HttpClient in another portion of the failing app and it works there.
I can change the code to use a HttpWebRequest and that works fine. Just not the HttpClient.
I just now discovered that if I copy the method into my button_click handler, it works there too. Is there a problem having this method inside a separate class? That seems odd to me.
update
What seems to be breaking it is multiple layers of async methods calling it. Within the class I have
public override byte[] GetImageData(string imageUri)
{
return GetImageDataAsync(imageUri).Result;
}
public async Task<byte[]> GetImageDataAsync(string imageUri)
{
return await DownloadData(imageUri);
}
from my button_click handler, I'm calling GetImageData(uri). If I change that to await GetImageDataAsync(uri) it works.
Is Result not the correct property to reference in GetImageData?
Here's a test url "http://www.rei.com/pix/common/REI_logo.gif"
Calling Result or Wait can cause deadlocks, as I explain on my blog.
The proper way to solve this is to use await. I assume that there's some reason you want to synchronously block, but it's better to use await and find a way to make it work in your code.