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.
Related
I use Xamarin Auth to authenticate with OneDrive in my android app. I assume this would work, but I have the issue that when the activity for the login prompt is opened the programm will just continue and not wait for the auth to finish.
How can I wait until it's closed or wrap this in a other way async?
Code:
private IDictionary<string, string> authenticationResponseValues;
protected override async Task<AccountSession> GetAuthenticationResultAsync()
{
await Task.Run(() => ShowWebView());
return new AccountSession(authenticationResponseValues, this.ServiceInfo.AppId, AccountType.MicrosoftAccount)
{
CanSignOut = true
};
}
private void ShowWebView()
{
var auth = new OAuth2Authenticator(
clientId: MSA_CLIENT_ID,
scope: string.Join(",", scopes),
authorizeUrl: new Uri(GetAuthorizeUrl()),
redirectUrl: new Uri(RETURN_URL));
auth.Completed += SetAccountInfos;
var intent = auth.GetUI(Application.Context);
intent.SetFlags(ActivityFlags.NewTask);
Application.Context.StartActivity(intent);
}
private void SetAccountInfos(object sender, AuthenticatorCompletedEventArgs eventArgs)
{
if (eventArgs.IsAuthenticated)
{
Debug.WriteLine(eventArgs);
Debug.WriteLine(eventArgs.Account == null ? "IS NULL" : "IS NOT NULL");
if (eventArgs.Account != null)
{
OAuthErrorHandler.ThrowIfError(eventArgs.Account.Properties);
authenticationResponseValues = eventArgs.Account.Properties;
}
}
}
I dont think using async tactic is reasonable, because app runs before login result returns.
Try to use sync way. Make a login page. If success, then switch to your real app.
I found a solution. Here my code:
await ShowWebView();
return new AccountSession(authenticationResponseValues, ServiceInfo.AppId,
AccountType.MicrosoftAccount)
{
CanSignOut = true
};
private Task<bool> ShowWebView()
{
var tcs = new TaskCompletionSource<bool>();
var auth = new OAuth2Authenticator(OneDriveAuthenticationConstants.MSA_CLIENT_ID, string.Join(",", OneDriveAuthenticationConstants.Scopes), new Uri(GetAuthorizeUrl()),
new Uri(OneDriveAuthenticationConstants.RETURN_URL));
auth.Completed += (sender, eventArgs) =>
{
if (eventArgs.IsAuthenticated)
{
OAuthErrorHandler.ThrowIfError(eventArgs.Account.Properties);
authenticationResponseValues = eventArgs.Account.Properties;
tcs.SetResult(true);
}
};
var intent = auth.GetUI(Application.Context);
intent.SetFlags(ActivityFlags.NewTask);
Application.Context.StartActivity(intent);
return tcs.Task;
}
And the link to the class in the repo: https://github.com/Apply-Solutions/MoneyManager/blob/master/Src/MoneyManager.Droid/Src/AndroidAuthenticationProvider.cs
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.
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.
I have to load two large files in parallels
so far I have this code
The code below is click button method
private async void MILoadLogFile_Click(object sender, RoutedEventArgs e)
{
...
if (oFD.ShowDialog() == true)
{
await myLogSession.LoadCompassLogAsync(oFD.FileName);
await myLogSession.LoadCoreServiceLogAsync(oFD.FileName);
}
}
loading method:
public async Task LoadCompassLogAsync(String fileName)
{
StreamReader streamReader = new StreamReader(fileName);
if (fileName.Contains("Compass"))
{
...
try
{
using (streamReader)
{
//Console.Out.WriteLine("lineCount: " + lineCount);
while (((line = await streamReader.ReadLineAsync()) != null)
&& !CompassLogLoadCompleted)
{
...
loggingLvl = new LoggingLvl(eLoggingLvl);
CompassLogData cLD = new CompassLogData(id, dateTime, loggingLvl, threadId, loggingMessage);
await addRoCompassLogCollectionAsync(cLD);
}
}
}
catch (Exception e)
{
Console.WriteLine("The file could not be read:");
Console.WriteLine(e.Message);
}
}
}
the LoadCoreServiceLogAsync is almost identical to LoadCompassLogAsync.
The two loading methods runs sequentially. I want them to run in parallel.
Your code will run one task after the other. To run the two tasks in parallel you can use the Task.WaitAll method:
var loadCompassLogTask = myLogSession.LoadCompassLogAsync(oFD.FileName);
var loadCoreServiceLogTask = myLogSession.LoadCoreServiceLogAsync(oFD.FileName);
Task.WaitAll(loadCompassLogTask, loadCoreServiceLogTask);
Or if you want to use await you can use Task.WhenAll:
var loadCompassLogTask = myLogSession.LoadCompassLogAsync(oFD.FileName);
var loadCoreServiceLogTask = myLogSession.LoadCoreServiceLogAsync(oFD.FileName);
await Task.WhenAll(loadCompassLogTask, loadCoreServiceLogTask);