Calling async method on button click - c#

I created Windows Phone 8.1 project and I am trying to run async method GetResponse<T>(string url) on button click and waiting for the method to finish, but method is never finishing. Here is my code:
private void Button_Click(object sender, RoutedEventArgs
{
Task<List<MyObject>> task = GetResponse<MyObject>("my url");
task.Wait();
var items = task.Result; //break point here
}
public static async Task<List<T>> GetResponse<T>(string url)
{
List<T> items = null;
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
try
{
Stream stream = response.GetResponseStream();
StreamReader strReader = new StreamReader(stream);
string text = strReader.ReadToEnd();
items = JsonConvert.DeserializeObject<List<T>>(text);
}
catch (WebException)
{
throw;
}
return items;
}
It will hang on task.Wait().
I changed my button click method to async and used await before the async method and I get the result(await GetResponse<string>("url")). What is wrong with Task<List<string>> task = GetResponse<string>("url")?
What am I doing wrong?
Thanks for the help!

You're the victim of the classic deadlock. task.Wait() or task.Result is a blocking call in UI thread which causes the deadlock.
Don't block in the UI thread. Never do it. Just await it.
private async void Button_Click(object sender, RoutedEventArgs
{
var task = GetResponseAsync<MyObject>("my url");
var items = await task;
}
Btw, why are you catching the WebException and throwing it back? It would be better if you simply don't catch it. Both are same.
Also I can see you're mixing the asynchronous code with synchronous code inside the GetResponse method. StreamReader.ReadToEnd is a blocking call --you should be using StreamReader.ReadToEndAsync.
Also use "Async" suffix to methods which returns a Task or asynchronous to follow the TAP("Task based Asynchronous Pattern") convention as Jon says.
Your method should look something like the following when you've addressed all the above concerns.
public static async Task<List<T>> GetResponseAsync<T>(string url)
{
HttpWebRequest request = (HttpWebRequest)HttpWebRequest.Create(url);
var response = (HttpWebResponse)await Task.Factory.FromAsync<WebResponse>(request.BeginGetResponse, request.EndGetResponse, null);
Stream stream = response.GetResponseStream();
StreamReader strReader = new StreamReader(stream);
string text = await strReader.ReadToEndAsync();
return JsonConvert.DeserializeObject<List<T>>(text);
}

This is what's killing you:
task.Wait();
That's blocking the UI thread until the task has completed - but the task is an async method which is going to try to get back to the UI thread after it "pauses" and awaits an async result. It can't do that, because you're blocking the UI thread...
There's nothing in your code which really looks like it needs to be on the UI thread anyway, but assuming you really do want it there, you should use:
private async void Button_Click(object sender, RoutedEventArgs
{
Task<List<MyObject>> task = GetResponse<MyObject>("my url");
var items = await task;
// Presumably use items here
}
Or just:
private async void Button_Click(object sender, RoutedEventArgs
{
var items = await GetResponse<MyObject>("my url");
// Presumably use items here
}
Now instead of blocking until the task has completed, the Button_Click method will return after scheduling a continuation to fire when the task has completed. (That's how async/await works, basically.)
Note that I would also rename GetResponse to GetResponseAsync for clarity.

#ChrisWalsh: If you use Task.Run() and call the async task inside that function, the task will run on a new UI thread and prevent blocking your UI.

use below code
Task.WaitAll(Task.Run(async () => await GetResponse<MyObject>("my url")));

Related

C# Async and Sync functions confuse me

I have an app in which a button starts creating XMLs. In the end of each XML creation, the SendInvoice function sends it, receives the response and a function (ParseResponse) parses the responses and does the database operations needed.
The idea is that when all the XMLs are created and sent, the application must close.
The problem is that I have lost control with async and the application seems to close before it actually finishes all the jobs. Also XMLs are sent before the previous have been processed.
The ParseResponse function is not asynchronous.
Here is the SendInvoice function.
Can you suggest any good practise?
Thank you in advance.
public async void SendInvoice(string body)
{
Cursor.Current = Cursors.WaitCursor;
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
var uri = "https://xxxx.xxx/SendInvoices?" + queryString;
HttpResponseMessage response;
// Request body
byte[] byteData = Encoding.UTF8.GetBytes(body);
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
response = await client.PostAsync(uri, content);
string responsebody = await response.Content.ReadAsStringAsync();
ParseResponse(response.ToString());
ParseResponse(responsebody);
}
}
The rest of the code
private void button1_Click(object sender, EventArgs e)
{
For
{
......
SendInvoice(xml)
}
System.Windows.Forms.Application.Exit();
}
Since you are calling the method from an Event Handler, this is a case where async void is acceptable, change your Button Click handler method signature to use async, I also added some ConfigureAwait(false) to async method calls - best-practice-to-call-configureawait-for-all-server-side-code and why-is-writing-configureawaitfalse-on-every-line-with-await-always-recommended:
private async void button1_Click(object sender, EventArgs e)
{
//since you are using a for-loop, I'd suggest adding each Task
//to a List and awaiting all Tasks to complete using .WhenAll()
var tasks = new List<Task>();
FOR
{
......
//await SendInvoice(xml).ConfigureAwait(false);
tasks.Add(SendInvoice(xml));
}
await Task.WhenAll(tasks).ConfigureAwait(false);
System.Windows.Forms.Application.Exit();
}
AND change your SendInvoice method signature to return a Task
public async Task SendInvoice(string body)
{
Cursor.Current = Cursors.WaitCursor;
var client = new HttpClient();
var queryString = HttpUtility.ParseQueryString(string.Empty);
var uri = "https://xxxx.xxx/SendInvoices?" + queryString;
HttpResponseMessage response;
// Request body
byte[] byteData = Encoding.UTF8.GetBytes(body);
using (var content = new ByteArrayContent(byteData))
{
content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
response = await client.PostAsync(uri, content).ConfigureAwait(false);
string responsebody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
ParseResponse(response.ToString());
ParseResponse(responsebody);
}
}
I was very used to multithreaded programming and it took me some time to understand asynchronous programming, because it really has nothing to do with multithreading. It is about doing more with a single thread, or a small number of threads.
asynchronous code is beneficial when the CPU would otherwise be waiting for something besides processing. Examples are: waiting for a network response, waiting for data to be read from disk, waiting on a separate process such as a database server.
It provides a way for the thread you are running to do other things while you wait. C# does this using Task. A task is some work that is being done, and it can be running or it can be waiting, and when waiting it doesn't need a thread attached.
All asynchronous functions must return a Task to be useful. So your function should be:
public async Task SendInvoice() {
...
The async keyword is used by the compiler to automatically wrap your function in a task object so you don't need to worry about a lot of the details. You just use await when calling another async function. You could do more work yourself to create tasks or return a task from another async function, or even call multiple async functions and await all of them together.
If your async method returns a value, use the generic Task: Task<String>, for example.
The Task is returned from an async method before the task completes. That is what allows the thread to be used by something else, but it has to get back to that starting place, which is why asynchonous programming you'll hear "async all the way up". It doesn't really do any good until it gets back to a caller that has multiple tasks to balance, which is usually the entry point of your application or the web request.
You can make your C# Main method async, but it mostly won't matter unless your process is really doing multiple things at the same time. For a web application, that can just be handling multiple requests. For a standalone app, it means you can query multiple APIs, make multiple web requests or db queries at the same time, and await them all, just using a single thread. Obviously, that can make things faster (at least locally, the external resources may have more work to do).
For a simple way to keep your program from exiting, if you have an asynchronous main, just await the call to SendInvoice. If your main is not async, you can use something like:
SendInvoice().Wait()
or
SendInvoice().Result
Using Wait() or Result will lock the thread until the task completes. It typically will make that thread exclusively available to the task so the thread cannot be used for any other tasks. If there are more threads in the threadpool, other tasks may continue to run, but typically using Wait/Result on a single Task defeats the point of asynchronous programming, so keep that in mind.
EDIT
Now that you have posted your calling code, it appears your call is in a loop. This is a good opportunity to take advantage of async calls and send ALL the invoices at once.
private async void button1_Click(object sender, EventArgs e)
{
List<Task> tasks = new List<Task>();
FOR
{
......
t = SendInvoice(xml).ConfigureAwait(false);
tasks.Add(t)
}
await Task.WhenAll(tasks).ConfigureAwait(false);
System.Windows.Forms.Application.Exit();
}
That will send ALL the invoices, then return from the handler, and then exit once all the responses have been received.

HttpClientt.SendAsync doesnt wait/lock execution

var httpResponseMessage = await httpClient.SendAsync(message).ConfigureAwait(false);
var dataStream = await httpResponseMessage.Content.ReadAsStreamAsync();
This by idea should be awaited, but no matter what it do executions exists the method and returns to UI. Execution resumes when responses arrives, but by that time UI has already updated that execution finished, when in fact it hasn't.
All calling methods are awaited.
Initial method is not awaited by design (Task.Run(() => StartDownload(selectedSchedules)); which starts UI method executing services that triggers httpclient, when that call finished UI should update with progress, but the second httpClient.SendAsyncis executed, execution returns to UI
Task.Run(() => StartDownload(selectedSchedules)); //First call, initiated by a button
public async Task StartDownload(SchedulesList list)
{
//var t = new Task(() => _scheduleServices.Download(list));
//t.Start();
//await t;
await _scheduleServices.DownloadIwcfDb(list, UsbInfoModels);
}
public async Task Download(SchedulesList schedulesList)
{
await DownloadDb(schedulesList);
}
private async Task DownloadDb(SchedulesList schedulesList)
{
using (var httpClient = new HttpClient())
{
var message = new HttpRequestMessage(new HttpMethod("POST"), ApiCallUrls.GetIwcfSchedules)
{
Content = new StringContent(JsonConvert.SerializeObject(schedulesList), Encoding.UTF8, "application/json")
};
httpClient.Timeout = TimeSpan.FromMinutes(20);
var httpResponseMessage= await httpClient.SendAsync(message).ConfigureAwait(false);
var dataStream = await httpResponseMessage.Content.ReadAsStreamAsync();
using (Stream contentStream = dataStream, stream = new FileStream(Path.Combine(Directories.SomEDir, Directories.SomeFileName), FileMode.Create, FileAccess.Write, FileShare.None))
{
await contentStream.CopyToAsync(stream);
}
}
}
Call Chain Added, irrelevant code removed from the methods
You're problem probably lies within your first call.
In your code you have:
Task.Run(()=>StartDownload(selectedSchedules)); //First call, initiated by a button
//I assume afterwards you update the ProgressBar or give some other progress feedback
What this does is: It calls StartDownload and immediately continues execution. All the other stuff (downloading etc) is then happening in the background. Remember that the method StartDownload does not block; it simply returns a Task object. If you do not await that Task object, the code will simply proceed.
I guess what you wanted is: Call StartDownload, wait for it to finish, then update the progress.
A quick solution would be to mark your event handler of the button with async, and then use async all the way. The method would look a bit like this:
private async void HandleEvent()
{
await StartDownload();
//update progress
}
I can recommend you this blog post from Stephen Cleary for an introduction to async-await: https://blog.stephencleary.com/2012/02/async-and-await.html

Run HttpClient PostAsync in another Class from MainWindow Background worker?

Preface: I'm a hobbyist so pardon my ignorance please ;-)
I'm trying to perform a web based multifunction task (online astrometry plate solving) that is located in a separate Class from my main window. I would like to do this in the background to keep the main window active (for logging messages in a scrolling LogTextBox).
In my Main Window I call this:
public void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Astrometry ast = new Astrometry();
ast.OnlineSolve(GlobalVariables.SolveImage);
}
And in the Astrometry Class it stops at the await httpClient.PostAsync(...
returns to main window and nothing else happens
class Astrometry
{
public void OnlineSolve(string image)
{
GetSession(apikey);
}
private async void GetSession(string apikey)
{
...misc code
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpContent contentPost = new StringContent(input, Encoding.UTF8, "application/x-www-form-urlencoded");
using (var response = await httpClient.PostAsync(baseAddress, contentPost))
{
string responseData = await response.Content.ReadAsStringAsync();
...more stuff
}
}
I'm wondering if this is just not possible to do this way...
Thanks in advance!
When using async/await, you must make sure to always propagate the tasks.
public async Task OnlineSolve(string image)
{
await GetSession(apikey).ConfigureAwait(false);
}
private async Task GetSession(string apikey)
{
...misc code
using (var httpClient = new HttpClient())
{
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpContent contentPost = new StringContent(input, Encoding.UTF8, "application/x-www-form-urlencoded");
using (var response = await httpClient.PostAsync(baseAddress, contentPost).ConfigureAwait(false))
{
string responseData = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
...more stuff
}
}
}
and again in the worker:
public async void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
Astrometry ast = new Astrometry();
await ast.OnlineSolve(GlobalVariables.SolveImage);
}
The main rule is don't break the await chain. As soon as you break it, you lose track of the asynchronous operation, and you lose your operation to wait for the result and handle any errors.
Also note that since you're using pure asynchronous I/O, you don't really need to use a background worker in the first place. Just run the asynchronous method directly from your button click handler or whatever, and you'll be fine. If you need to do some CPU work as well, Task.Run is a decent option. I'm not even sure if background worker handles marshalling the await back to the proper thread correctly (EDIT: And indeed, it doesn't; so there really isn't any point in using BackgroundWorker with await, it's just confusing - just use Task.Run for CPU work, and await for asynchronous I/O).
The thing to keep in mind is that await basically functions as a magical return (similar to a yield return, if you've ever used that). As soon as execution reaches an await that awaits a task that isn't complete, the method returns a Task (if possible). That's the point where the caller gets flow control back - and if you don't use await in the caller, that's the state the work is in when your caller continues execution. Sometimes, this is desirable - for example, when launching multiple tasks in parallel. Usually, you just want to await all asynchronous methods immediately.

Async / await on windows form application

I'm trying to learn and implement async / await keywords on my application. I'm using an API to get data then showing them on my forms. When I try to call methods from an console application there is no problem. But if I call my async methods from Form_Shown event also there no exception but methods not working.
So I'm calling my RefreshOrLoadDataToCache() method on Form_Shown event.
private async void LogTimeReport_Shown(object sender, EventArgs e)
{
// Some syncronous operations
RefreshOrLoadDataToCache(); // Async methods in it
// Some syncronous operations
}
In my this method created a task and wait for it.
private async void RefreshOrLoadDataToCache()
{
if (IsNeededToCallAPI())
{
var taskForTimeEntries = LoadTimeEntriesTemp();
Task.WhenAll(taskForTimeEntries);
DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;
DataTable dtEventsTemp = LoadEventsTemp();
dtTimeEntriesTemp.Merge(dtEventsTemp);
}
else
BindGridViews();
}
This my async method.
private async Task<DataTable> LoadTimeEntriesTemp()
{
TimeEntryHandler timeHandler = new TimeEntryHandler();
TimeEntryResponse response = await timeHandler.GetTimeEntries();
DataTable dt = DatatableHelper.ToDataTable<TimeEntry>(response.TimeEntries);
foreach (DataRow drow in dt.Rows)
{
// Some operations on DataTable
}
return dt;
}
In this method I'm connecting to API and getting results. I think my problem is about this method. Because when I call this method from console application it returns data. But from form application it waits for a long time but there is no result or exception.
private async Task<TimeEntryResponse> GetTimeEntries()
{
using (var client = new AuthorizedHttpClient(_client))
{
var data = await client.GetAsync<TimeEntryResponse>(parameters);
if (data.StatusCode == HttpStatusCode.OK)
{
var response = (TimeEntryResponse)data.ContentObj;
response.Pages = int.Parse(data.Headers.GetValues("X-Pages").First());
response.Page = int.Parse(data.Headers.GetValues("X-Page").First());
response.TotalRecords = int.Parse(data.Headers.GetValues("X-Records").First());
return response;
}
return new TimeEntryResponse() { TimeEntries = null, STATUS = "ERROR" };
}
}
I thought that there is something I'm missing about asyncronous calls on windows forms. How can I fix my code ?
You have a couple of problems with your code
You mark a method as async, but you don't await on the operation inside. You currently do this because RefreshOrLoad is async void. It actually needs to be async Task, where the underlying returned task is the ongoing async operation. Then, the returned Task should be awaited on:
private async void LogTimeReport_Shown(object sender, EventArgs e)
{
// Some syncronous operations
await RefreshOrLoadDataToCache(); // Async methods in it
// Some syncronous operations
}
RefreshOrLoad is an async method. You use Task.WhenAll, which is used for asynchronously waiting on multiple tasks, but you don't await on it either. Then, you call .Result, which causes your code to effectively deadlock. All that's needed is to await the task returning from LoadTimeEntriesTemp:
private async Task RefreshOrLoadDataToCache()
{
if (IsNeededToCallAPI())
{
DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();
DataTable dtEventsTemp = LoadEventsTemp();
dtTimeEntriesTemp.Merge(dtEventsTemp);
}
else
BindGridViews();
}
I'd also note that you should use the *Async postfix with your async methods.
When fixing these, you'll see that your code behaves as expected, being asynchronous all the way down.
You problem here:
var taskForTimeEntries = LoadTimeEntriesTemp();
Task.WhenAll(taskForTimeEntries);
DataTable dtTimeEntriesTemp = taskForTimeEntries.Result;
At first, why do you use Task.WhenAll when you have just one task? That way you leak the task returned by Task.WhenAll which will be completed and indicate that all your tasks that are passed to Task.WhenAll are completed. It will wait for the task synchronously which will cause deadlock.
There is a rule for async/await which states await in all ways
So right approach is:
DataTable dtTimeEntriesTemp = await LoadTimeEntriesTemp();
Also, you should await on RefreshOrLoadDataToCache(); in your event handler if you want to do synchronous operations related to its result.
Here is a great article by Stephen Cleary Don't Block on Async Code which describes your problem in more details.
Method RefreshOrLoadDataToCache() is marked as async yet it does not use await on Task.WhenAll() and LogTimeReport_Shown() does not have to be async. :
private async void RefreshOrLoadDataToCache()
{
if (IsNeededToCallAPI())
{
var taskForTimeEntries = LoadTimeEntriesTemp();
DataTable dtTimeEntriesTemp = await taskForTimeEntries; // call await here
DataTable dtEventsTemp = LoadEventsTemp();
dtTimeEntriesTemp.Merge(dtEventsTemp);
}
else
BindGridViews();
}

How to cancel an async call without checking the cancellation pending property (Instantly terminating the method call)

I can't find a way to cancel an async call running on the background thread.
Let's say I have something like this:
private async void myMethod()
{
String result = await Task.Run(async () => await myTimeConsumingMethod());
//Do stuff with my result string...
}
Here I'm calling an async method on my background thread, and since I don't have any loops I can't do something like this:
for (i = 0; i < someBigNumber; i++)
{
token.ThrowIfCancellationRequested();
await doSomeWork();
}
Instead I have a single call to an async method that can take more than 10 seconds to complete, I can't check if the token has been canceled inside that method, since I'm awaiting for the async call to complete.
This is what I'm trying to achieve:
private async void myMethod()
{
String result = await Task.Run(async () => await myTimeConsumingMethod());
//Override the HardwareButtons.BackPressed event (I already have a method for that)
//When the await is completed, the String is the result of the async
//call if the method has completed, otherwise the result String
//should be set to null.
}
The problem is that I don't know what code to use inside the HardwareButtons.BackPressed event to terminate my async call.
I mean, I literally want to "terminate its process" and make that Task.Run instantly return null.
Is there a way to do that?
This is the implementation of myTimeConsumingMethod():
public static async Task<String> ToBase64(this StorageFile bitmap)
{
IRandomAccessStream imageStream = await CompressImageAsync(bitmap);
BitmapDecoder decoder = await BitmapDecoder.CreateAsync(imageStream);
PixelDataProvider pixels = await decoder.GetPixelDataAsync();
byte[] bytes = pixels.DetachPixelData();
return await ToBase64(bytes, (uint)decoder.PixelWidth, (uint)decoder.PixelHeight, decoder.DpiX, decoder.DpiY);
}
This is impossible. You can create a Task that will complete when that existing operation completes, or mark itself as cancelled if a cancellation token is cancelled, but that won't actually stop the original operation, merely allow the program to continue executing despite the fact that the operation isn't actually done yet.
See this blog article for more information on the subject.

Categories

Resources