I'm working with async tasks for the first time, and I'm confronted with a problem I can't seem to solve.
i've got a method to call an API using a Http request.
internal static async Task<HttpResponse> CallAPI(string objectname, string parameters, HttpMethod method)
{
HttpResponse r = new HttpResponse();
using (r.HttpClient = new HttpClient())
{
r.HttpClient.BaseAddress = new Uri("https://" + Properties.Settings.Default.APIURL + "/");
r.HttpClient.DefaultRequestHeaders.Clear();
r.HttpClient.DefaultRequestHeaders.Add("Accept", "application/vnd+json;version=1");
r.HttpClient.DefaultRequestHeaders.Add("Host", Properties.Settings.Default.APIURL);
r.HttpClient.DefaultRequestHeaders.Add("Expect", "100-continue");
r.HttpClient.DefaultRequestHeaders.Add("Connection", "Close");
switch (method)
{
case HttpMethod.DELETE:
using (r.ResponseMessage = await r.HttpClient.DeleteAsync("api/" + objectname.ToString() + "/" + parameters))
{
var stopwatch = Stopwatch.StartNew();
r.responseTime = Convert.ToInt32(stopwatch.ElapsedMilliseconds);
r.ResponseData = await r.ResponseMessage.Content.ReadAsStringAsync();
return r;
}
case HttpMethod.GET:
using (r.ResponseMessage = await r.HttpClient.GetAsync("api/" + objectname.ToString() + parameters))
{
var stopwatch = Stopwatch.StartNew();
r.responseTime = Convert.ToInt32(stopwatch.ElapsedMilliseconds);
r.ResponseData = await r.ResponseMessage.Content.ReadAsStringAsync();
return r;
}
default: throw new Exception("No HTTP Method Found");
}
}
I call a delete() method in the class from a button click event:
protected void btnDelete_Click(object sender, EventArgs e)
{
Activity a = new Activity();
a.Id = Convert.ToInt32(txtObjectId.text);
//a.Delete(); //void method
bool done = a.Delete().Result; //Task<bool> method
}
if I make the delete() method a void, it works fine and returns the http response
public async virtual void Delete()
{
HttpResponse r = new HttpResponse();
r = await CallAPI(_Objectname.ToString(), _Id.ToString(), HttpMethod.DELETE);
}
but if I try to make Delete() a Task,
public async virtual Task<bool> Delete()
{
try
{
HttpResponse r = new HttpResponse();
r = await CallAPI(_Objectname.ToString(), _Id.ToString(), HttpMethod.DELETE);
return true;
}
catch
{
return false;
}
}
it tries to execute the httpclient.deleteasync method and the app just doens't do anything. I don't get an exception, it doens't freeze, it just doens't seem to do anything anymore.
I have no idea what's causing this behavior, but I'm fairly new to async programming, so I'm probably doing something which I'm not supposed to :-)
If you do .Result or .Wait() on code that uses async/await you will deadlock your program, you must make your event handler async void, this is the only place you are allowed to use async void.
protected async void btnDelete_Click(object sender, EventArgs e)
{
Activity a = new Activity();
a.Id = Convert.ToInt32(txtObjectId.text);
//await a.Delete(); //Task method
bool done = await a.Delete(); //Task<bool> method
}
And in case you do end up using the version that does not return Task<bool> the Delete() function would look like
public async virtual Task Delete()
{
HttpResponse r = new HttpResponse();
r = await CallAPI(_Objectname.ToString(), _Id.ToString(), HttpMethod.DELETE);
}
you do not need a return statement.
Also, if you don't need to access the UI, add .ConfigurateAwait(false) to each await call to make it not force the UI thread for it's continuations.
public async virtual Task Delete()
{
HttpResponse r = new HttpResponse();
r = await CallAPI(_Objectname.ToString(), _Id.ToString(), HttpMethod.DELETE).ConfigureAwait(false);
}
You can do the same to CallAPI too to make it run better.
Related
This question already has answers here:
An async/await example that causes a deadlock
(5 answers)
Closed 4 years ago.
When you create a new cosmos DB some code samples are shown in Azure. Most of the code works. However I have this single function that just freezes/hangs when Executed. Any ideas how to solve or troubleshoot?
Code:
public async Task<IEnumerable<T>> GetItemsAsync(Expression<Func<T, bool>> predicate, string collection)
{
IDocumentQuery<T> query = Client.CreateDocumentQuery<T>(
UriFactory.CreateDocumentCollectionUri(DatabaseId, collection),
new FeedOptions { MaxItemCount = -1, EnableCrossPartitionQuery = true })
.Where(predicate)
.AsDocumentQuery();
List<T> results = new List<T>();
while (query.HasMoreResults)
{
try
{
var result = await query.ExecuteNextAsync<T>();
results.AddRange(result);
}
catch(Exception ex)
{
}
}
return results;
}
Calling the function:
return await ItemsRepo.GetItemsAsync(p=>p.RunId == runId, "files-and-folders-with-unique-permissions");
Finally the windows app:
var items = _db.GetFilesAndFoldersWithUniquePermissions(txtRunId.Text).Result;
Full code:
Repository:
public async Task<IEnumerable<Item>> GetFilesAndFoldersWithUniquePermissions(string runId)
{
var collectionLink = UriFactory.CreateDocumentCollectionUri("kk-governance-reporting", "files-and-folders-with-unique-permissions");
return await ItemsRepo.GetItemsAsync(p=>p.RunId == runId, "files-and-folders-with-unique-permissions");
}
App:
private void BtnGenerateReport_Click(object sender, EventArgs e)
{
var items = _db.GetFilesAndFoldersWithUniquePermissions(txtRunId.Text).Result;
string json = JsonConvert.SerializeObject(items.ToArray());
json = JToken.Parse(json).ToString(Formatting.Indented);
//write string to file
System.IO.File.WriteAllText(#"c:\temp\unique-files-and-folders.txt", json);
MessageBox.Show("Completed");
}
Mixing async-await and .Result blocking calls can cause deadlocks.
In this case, event handlers allow for async void.
Reference Async/Await - Best Practices in Asynchronous Programming
So I suggest you refactor the App to await in the event handler
private async void BtnGenerateReport_Click(object sender, EventArgs e) {
var items = await _db.GetFilesAndFoldersWithUniquePermissions(txtRunId.Text); //<--
string json = JsonConvert.SerializeObject(items.ToArray());
json = JToken.Parse(json).ToString(Formatting.Indented);
//write string to file
System.IO.File.WriteAllText(#"c:\temp\unique-files-and-folders.txt", json);
MessageBox.Show("Completed");
}
To allow the code to flow as expected.
I have the need to move some processes to async. I have 5 methods that I need to call individually and have run in the background so the user can continue on with their work.
The test code below seems to work... but I haven't been able to figure out how to return information (message) indicating that the a task has completed. The class will be called from a separate windows form so that the progress can be displayed....
from the form:
async void BtnGo_Click(object sender, System.EventArgs e)
{
label2.Text = #"Starting tasks...";
var progress = new Progress<string>(
p =>
{
label2.Text = p;
});
await TestTask.MyTestMain(progress);
}
the class:
public static class TestTask
{
public static Task MyTestMain(IProgress<string> pProgress)
{
return SomethingAsync(pProgress);
}
private static async Task SomethingAsync(IProgress<string> pProgress)
{
var t1 = SomeThing1(pProgress);
var t2 = SomeThing2(pProgress);
await Task.WhenAll(t1, t2);
if (pProgress != null) pProgress.Report(#"all tasks completed");
}
private static async Task SomeThing1()
{
await Task.Delay(9000);
var filename = #"c:\temp\tt1.txt";
if (File.Exists(filename))
File.Delete(filename);
using (TextWriter tw = new StreamWriter(filename))
{
await tw.WriteLineAsync(DateTime.Now.ToLongDateString());
}
if (pProgress != null) pProgress.Report(#"t1 completed");
}
private static async Task SomeThing2()
{
await Task.Delay(7000);
var filename = #"c:\temp\tt2.txt";
if (File.Exists(filename))
File.Delete(filename);
using (TextWriter tw = new StreamWriter(filename))
{
await tw.WriteLineAsync(DateTime.Now.ToLongDateString());
}
if (pProgress != null) pProgress.Report(#"t2 completed");
}
}
I would like know when each task has completed. Any help or direction would be appreciated.
EDIT
I have edited this post to reflect my changes... I still cannot get a progress report back to the UI... any thoughts?
You're doing IO bound work, you don't need to use thread-pool threads.
Transform your methods to use the async APIs of StreamWriter:
private static async Task FirstThingAsync()
{
var filename = #"c:\temp\tt1.txt";
if (File.Exists(filename))
File.Delete(filename);
using (TextWriter tw = new StreamWriter(filename))
{
await tw.WriteLineAsync(DateTime.Now);
}
}
Same for your second method, and then you can asynchronously wait on them concurrently:
private static async Task SomethingAsync()
{
var firstThing = FirstThingAsync();
var secondThing = SecondThingAsync();
await Task.WhenAll(firstThing, secondThing);
}
Edit:
You're never reaching your first Progress.Report call because your code is throwing an InvalidOperationException when you call t.Start() on a promise-style task:
t1.Start();
await t1;
t2.Start();
await t2;
The task returned from both method calls is a "hot task", meaning it's operation is already started. The docs on Task.Start say:
InvalidOperationException: The Task is not in a valid state to be
started. It may have already been started, executed, or canceled, or
it may have been created in a manner that doesn't support direct
scheduling.
The reason you're not seeing that exception is because you're swallowing it:
var t = SomethingAsync(pProgress);
When you don't await on the async operation. Your method calls should look like this:
public static Task MyTestMain(IProgress<string> pProgress)
{
return SomethingAsync(pProgress);
}
async void BtnGo_Click(object sender, System.EventArgs e)
{
label2.Text = #"Starting tasks...";
var progress = new Progress<string>(
p =>
{
label2.Text = p;
});
await TestTask.MyTestMain(progress);
}
I'm trying to show a waiting symbol while while a ASYNC task are doing.
I'm really new to this, so if there are better ways to implement this code, please enlighten me :)
But, everything works except the hiding of the pictureBox1 after the code are done and there are now result found. In other words, when there are a result, the pictureBox1 are hidden
Here are the method that runs every time a outlook item are opened
private void FormRegion1_FormRegionShowing(object sender, System.EventArgs e)
{
if (this.OutlookItem is Microsoft.Office.Interop.Outlook.MailItem)
{
Microsoft.Office.Interop.Outlook.MailItem item = (Microsoft.Office.Interop.Outlook.MailItem)this.OutlookItem;
getContactByEmail(item);
}
}
This is the method that I implement the wait stuff
public async Task getContactByEmail(Microsoft.Office.Interop.Outlook.MailItem item)
{
pictureBox1.Visible = true;
using (var client = new System.Net.Http.HttpClient())
{
client.BaseAddress = new Uri("http://api.....");
client.DefaultRequestHeaders.Accept.Clear();
HttpResponseMessage response = await client.GetAsync("tools/getContactByEmail?email=" + item.SenderEmailAddress + "&key=1232");
if (response.IsSuccessStatusCode)
{
SimpleContact contact = await response.Content.ReadAsAsync<SimpleContact>();
lblName.Text = contact.Name;
lblMobile.Text = contact.Phone;
}
pictureBox1.Visible = false;
}
}
Posting the code that fixes this so the exception are not raised
if (response.IsSuccessStatusCode)
{
SimpleContact contact = await response.Content.ReadAsAsync<SimpleContact>();
if (contact != null)
{
lblName.Text = contact.Name;
lblMobile.Text = contact.Phone;
}
pictureBox1.Visible = false;
}
In C# method names are always CamelCase and asynchronous methods are always suffixed Async. Just conventions.
You might want to extract the non UI code to another asynchronous method to avoid going back and forth to the UI thread:
private async void FormRegion1_FormRegionShowing(object sender, System.EventArgs e)
{
if (this.OutlookItem is Microsoft.Office.Interop.Outlook.MailItem)
{
Microsoft.Office.Interop.Outlook.MailItem item = (Microsoft.Office.Interop.Outlook.MailItem)this.OutlookItem;
pictureBox1.Visible = true;
var contact = GetContactByEmailAsync(item);
if (contact != null)
{
lblName.Text = contact.Name;
lblMobile.Text = contact.Phone;
}
pictureBox1.Visible = false;
}
}
public async Task<SimpleContact> GetContactByEmailAsync(Microsoft.Office.Interop.Outlook.MailItem item)
{
using (var client = new System.Net.Http.HttpClient())
{
client.BaseAddress = new Uri("http://api.....");
client.DefaultRequestHeaders.Accept.Clear();
HttpResponseMessage response = await client.GetAsync(
"tools/getContactByEmail?email=" + item.SenderEmailAddress + "&key=1232")
.ConfigureAwait(false);
return (response.IsSuccessStatusCode)
? await response.Content.ReadAsAsync<SimpleContact>();
: null;
}
}
Note: Don't forget proper exception handling!!!
I have a windows phone 7 app with following code
ServiceReference1.SMSWarriorServiceSoapClient ws = new ServiceReference1.SMSWarriorServiceSoapClient();
ws.BalanceCompleted += new EventHandler<ServiceReference1.BalanceCompletedEventArgs>(ws_BalanceCompleted);
ws.BalanceAsync(textBox1.Text, textBox2.Password);
Now i want to make it also for Windows 8 store app. I try this
var client = new ServiceReference1.SMSWarriorServiceSoapClient() ;
var result = client.BalanceAsync("user", "pass");
resultDetails.Text = result.ToString ;
but with no luck
I also try the Await but I don't know how to use it
If you are calling the web service method from an event handle then use this.
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
var client = new ServiceReference1.SMSWarriorServiceSoapClient();
var result = await client.BalanceAsync("user", "pass");
resultDetails.Text = result.ToString();
}
If you are calling the web service method from a method then use this.
private async Task ServiceCall()
{
var client = new ServiceReference1.SMSWarriorServiceSoapClient();
var result = await client.BalanceAsync("user", "pass");
resultDetails.Text = result.ToString();
}
Ensure you call method ServiceCall() with await keyword.
if u use await then u must use async keyword or use Task
var client = new ServiceReference1.SMSWarriorServiceSoapClient() ;
var result = await client.BalanceAsync("user", "pass");
resultDetails.Text = result.ToString ;
for example:
// Put the keyword, async on the declaration of the event handler.
private async void Button_Click_1(object sender, RoutedEventArgs e)
{
var respose=await <some async operation >
}
Looking to chain a task to a previous instance if it exists. Currently, both are executed at the same time.
Initial code that works for one task :
private async void MenuMediaAddFiles_OnClick(object sender, RoutedEventArgs e)
{
var dialog = GetDefaultOpenFileDialog();
using (dialog)
{
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (var progress = new SimpleProgress(this))
{
int addFiles = await _context.AddFiles(dialog.FileNames, progress);
Console.WriteLine("Files added: {0}", addFiles);
}
}
}
}
A failed attempt to make it work :
Task<int> _files;
private async void MenuMediaAddFiles_OnClick(object sender, RoutedEventArgs e)
{
var dialog = GetDefaultOpenFileDialog();
using (dialog)
{
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (var progress = new SimpleProgress(this))
{
int addFiles;
Task<int> files = _context.AddFiles(dialog.FileNames, progress);
if (_files == null)
{
_files = files;
}
else
{
var task1 = await _files.ContinueWith(task => _context.AddFiles(dialog.FileNames, new SimpleProgress(this)));
}
addFiles = await _files;
Console.WriteLine("Files added: {0}", addFiles);
}
}
}
}
You were pretty close, but there were a few things that needed to be modified:
private Task<int> previousTask = Task.FromResult(0);
private async void MenuMediaAddFiles_OnClick(object sender, RoutedEventArgs e)
{
var dialog = GetDefaultOpenFileDialog();
using (dialog)
{
if (dialog.ShowDialog() == System.Windows.Forms.DialogResult.OK)
{
using (var progress = new SimpleProgress(this))
{
previousTask = previousTask.ContinueWith(t =>
_context.AddFiles(dialog.FileNames, progress))
.UnWrap(); ;
int addFiles = await previousTask;
Console.WriteLine("Files added: {0}", addFiles);
}
}
}
}
Things to note:
Rather than having the previous task be null sometimes, it was easier to initialize it to an already completed task (Task.FromResult(0)). This avoids the null check code.
You were calling AddFiles twice. You shouldn't have been calling it before the if, and you weren't ever assigning the task to the instance field inside the if.
I used UnWrap instead of await to turn the Task<Task<int>> into a Task<int>. Both work, but in this case I felt UnWrap made its intentions clearer.
Note that since the entire event handler will be running in the UI thread there's no need to synchronize access to previousTask, if it doesn't, you'd need to do some locking.