I cannot get the following event to await the result of ActionShared.BL.Managers.TaskManager.GetTask - it just continues - and the TaskDetailsPageViewController is never shown. Please advice if you can see the bug ??
public async void OnButtonPressedTask (object sender, CellUtilityButtonClickedEventArgs e)
{
if(e.UtilityButtonIndex == 1){
// Edit
var a = GetItem(e.IndexPath) as Outstanding;
APTask ab = new APTask();
ab.Id = Convert.ToInt32(a.Entity.Id);
await ActionShared.BL.Managers.TaskManager.GetTask (ab.Id, async (response) => {
var taskDetailsPage = new TaskDetailsPageViewController(response, e.IndexPath);
parent.NavigationController.PushViewController(taskDetailsPage, true);
});
}
}
Thanks guys
I changed ActionShared.BL.Managers.TaskManager.GetTask to use async/await in stead of a call back.
Related
I am trying to wait for the end of a task. This task loads a bunch of things from the server and usually takes about 2 to 3 seconds. This task is started by the command LoadItemsCommand. Once the task is over, it should check if anything has been loaded, and if any of the loaded items already contain values. If yes, it should remove a button from the Toolbar.
But it doesn't wait.
The command call and specifically ask to await the task, but on the main page, things procede without waiting for the end of the task. i tried to put a loop with 30 second wait time to wait for the command to work, but nothing.
Here is the command in the viewmodel:
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
Here is the LoadItems Function in the ViewModel
async Task ExecuteLoadItemsCommand()
{
if (IsBusy)
return;
IsBusy = true;
List<string> stockIds = new List<string>();
foreach (var orderline in this._order)
{
stockIds.Add(orderline.StockId);
}
string[] array = new string[stockIds.Count];
stockIds.CopyTo(array, 0);
this._products = await Common.GetProducts(new ProductSearchFilter { StockId = array });
_scanStart = Common.Now;
Items.Clear();
bool already_scanned = false; // Todo trouver un meilleur non car pas tres explicite
foreach (PickingOrderLineToHandle orderLine in _order)
{
if (orderLine.ScannedQuantity < orderLine.Quantity)
{
if ((bool)orderLine.WasSentToEnd && !already_scanned)
{
Items.Add(new ListItem() { Name = " ----- ", Id = "-1" });
already_scanned = true;
}
Product product = null;
foreach (var prod in _products)
{
if (prod.StockId == orderLine.StockId)
{
product = prod;
break;
}
}
Items.Add(new ListItem() { Name = $"{(orderLine.Quantity - orderLine.ScannedQuantity).ToString()} / {orderLine.Quantity}\t + {orderLine.Table_bin.LabelAddress} {product.ProductName} {orderLine.stock.Platform}", Id = orderLine.StockId });
}
}
IsBusy = false;
}
And here is the call in the page:
protected override void OnAppearing()
{
base.OnAppearing();
viewModel.LoadItemsCommand.Execute(null);
int loop = 0;
while (viewModel.IsBusy && loop < 60)
{
System.Threading.Thread.Sleep(500);
loop++;
};
if (loop == 60)
{
DisplayAlert("Erreur", "Une erreur est survenue lors du chargment. Veuillez réessayer.", "Ok");
Navigation.PopAsync();
}
var cantCancel = viewModel.Items.Any(i => i.BookedQuantity > 0);
if (Common.IsTeamLeader)
cantCancel = false;
if (cantCancel)
{
var cancelButton = this.ToolbarItems.Where(b => b.Text == "Annuler").First();
this.ToolbarItems.Remove(cancelButton);
}
}
Problem
The following statement seems like a blocking call, but actually, since the method that is called by the Command is an asynchronous anonymous function, it will just execute in a fire and forget fashion and will not be awaited:
viewModel.LoadItemsCommand.Execute(null);
That's because of how the Command is defined:
LoadItemsCommand = new Command(async () => await ExecuteLoadItemsCommand());
A Command like this always executes synchronously. However, when calling an async void method from within a synchronous context, it will just begin execution, but won't await the method (hence fire and forget).
Solution
Now, the way you're using the Command is unusual. Normally, you would bind to the Command from a UI control or you would just trigger its execution. You are expecting it to finish before the rest of the code is executed, which isn't the case as I've explained above.
Instead of executing the Command like that, you could instead do the following:
Make sure ExecuteLoadItemsCommand() is returning a Task
Change the visibility of ExecuteLoadItemsCommand() to public
Change the name to ExecuteLoadItemsAsync() which is a common notation for methods that return asynchronous Tasks
Your new method signature should look something like this:
public async Task ExecuteLoadItemsAsync()
{
//...
}
Then, in your page's code-behind, you could change the OnAppearing() method override to run asynchronously by adding the async keyword to the signature and then awaiting the ExecuteLoadItemsAsync() method:
protected override async void OnAppearing()
{
base.OnAppearing();
await viewModel.ExecuteLoadItemsAsync();
//...
}
This way, you're executing and awaiting the method directly. The Command can still be used for any UI elements to bind to, such as buttons.
Optional improvement
In your ViewModel, you could use an AsyncRelayCommand instead of Command like this:
private AsyncRelayCommand _loadItemsCommand;
public AsyncRelayCommand LoadItemsCommand => _loadItemsCommand ??= new AsyncRelayCommand(ExecuteLoadItemsAsync);
However, this doesn't change the fact that you shouldn't execute the Command in your page's code behind the way you've been attempting.
How to use _Asyn methods correctly ?
With my limited experience with C#, I don't know why. How to improve it? Thanks in advance.
//use :
this.SyncDataDataGridView.DataSource = await patentLaunch.LoadAccessDbListAsync();
//Method-01 winfroms UI OK (use ToList() )
public async Task<List<SyncNotice>> LoadAccessDbListAsync01()
{
return await Task.Run(() =>
{
using (var context = new SyncDbContextContext())
{
return context.SyncNotices.OrderByDescending(m => m.Id).Take(1000).ToList();
}
});
}
//Method-02 winfroms UI freeze ! ( use ToListAsync() EntityFrameworkCore )
public Task<List<SyncNotice>> LoadAccessDbListAsync02()
{
using (var context = new SyncDbContextContext())
{
return context.SyncNotices.OrderByDescending(m => m.Id).Take(1000).ToListAsync();
}
}
my full code is:
private async void Form_Load(object sender, EventArgs e){
//init binding
await context.SyncNotices.LoadAsync();
this.SyncDataDataGridView.DataSource = context.SyncNotices.Local.ToBindingList();
}
//init event
patentLaunch.OnFolderAddAccessOk += () => {
Invoke(new Action(async () =>
{
this.SyncDataDataGridView.DataSource = await patentLaunch.LoadAccessDbListAsync02();
}));
};
};
}
//call in fileSystemWatcher1_Created(object sender, System.IO.FileSystemEventArgs e)
//in patentLaunch.cs
public void AddSigleFloderToAccessDb(string folder, string syncStatus)
{
var now = DateTime.Now;
var entity = new SyncNotice
{
FolderName = folder,
CreateTime = now,
SyncStatus = syncStatus
};
using (var context = new SyncDbContextContext())
{
context.SyncNotices.Add(entity);
context.SaveChanges();
}
UpdateLastExecTime(now);
// send event
OnFolderAddAccessOk();
}
//in patentLaunch.cs
public event Action OnFolderAddAccessOk;
Thanks for JonasH . Thank you for your reply.
,finally I used mehtod01
Some databases do not support truly async queries. In that case queries may be synchronous even if there is a *Async-method.
I have not found any concrete documentation about access supporting or not supporting asynchronous queries. But based on its age, and the fact that your call is actually blocking, I would guess the chances are low.
So as far as I know you just have to use LoadAccessDbListAsync01 to avoid blocking.
Some databases do not support truly async queries. In that case queries may be synchronous even if there is a *Async-method.
I have not found any concrete documentation about access supporting or not supporting asynchronous queries. But based on its age, and the fact that your call is actually blocking, I would guess the chances are low.
So as far as I know you just have to use LoadAccessDbListAsync01 to avoid blocking.
I'm trying to get data from server and afterwards I need to do few things with that data and other functions.
Because I'm getting data from server I did it with async and continuewith functions.
This is my code:
private void login(object sender, EventArgs eventArgs)
{
SharedFunctions.showHide("Show", pBar, txt);
result = false;
if (validateScreen())
{
Task task = new Task(() => LoginUser().ContinueWith((t) =>
{
afterLogin();
}));
task.Start();
}
}
private void afterLogin()
{
if (result)
{
SharedFunctions.saveDataOnDevice(userID, storeID, permission);
StartActivity(typeof(SplashScreen));
Finish();
}
else
{
SharedFunctions.showHide("Hide", pBar, txt);
SharedFunctions.showPopUp(this, GetString(Resource.String.error_login), GetString(Resource.String.wrong_name_and_password));
}
}
private async Task LoginUser()
{
string userName = uName.Text;
string password = pass.Text;
password = SharedFunctions.encrypt(password);
var client = new RestClient("........");
string resourceStr = #"api/base/....";
var request = new RestRequest(Method.POST)
{
Resource = resourceStr,
RequestFormat = DataFormat.Json
};
request.AddBody(new { UserName = userName, Password = password });
var response = await client.ExecuteTaskAsync<dynamic>(request);
var dt = response.Data;
if (dt != null)
{
userID = dt["ID"];
storeID = dt["StoreID"];
permission = dt["Permission"];
result = true;
}
else
result = false;
}
My main problem is that after I get the data, right after this code: if (dt != null).
When I try to debug the code it reaches the row with userID = dt["ID"]; and even before it is executed it jumps to the afterLogin() function.
What do I need to change in my code to make it run the entire functions before going to the next?
Thank you in advance!
As I describe on my blog, you should never use the Task constructor or the Start method. They are extremely outdated ways to execute code on a thread pool thread, which your app doesn't even need to do since LoginUser is asynchronous. If you did need to execute code on a thread pool thread, the correct API is Task.Run, but in this case you don't need it.
As a side note, you should not use ContinueWith in this situation, either (also explained on my blog). In fact, ContinueWith is downright dangerous; you should use await instead.
After applying these best practices:
private async void login(object sender, EventArgs eventArgs)
{
SharedFunctions.showHide("Show", pBar, txt);
result = false;
if (validateScreen())
{
await LoginUser();
afterLogin();
}
}
When it jumps out that means that the Task breaks (exception) but it doesn't break the whole program as this is an async Task.
Make a breakpoint right threre and check what is inside dt.
Especially with strings like "ID" you often have errors like these. It might be as well "Id" or "id" which is not equal nill but also not matching your "ID".
Good luck!
The problem was very different.
I found out that I cannot convert var that was in the dynamic object to int and that is why the program stopped.
I change this row: userID = dt["ID"]; to this: userID = Convert.ChangeType(dt["ID"], typeof(int));
And now it works.
Thank you all for all of your suggestions.
Task.ContinueWith starts only after completion of the original task.
I'm not sure if this'll help you but try this
private void login(object sender, EventArgs eventArgs)
{
SharedFunctions.showHide("Show", pBar, txt);
result = false;
if (validateScreen())
{
Task.Factory.StartNew(() => { LoginUser(); }).ContinueWith((t) => { afterLogin(); });
}
}
And make your login a normal void function.
Attempting to get a filtered stream using the Tweetinvi library via c#.
I am trying to do something similar to the search, which I have been able to successfully receive tweets:
public List<ITweet> SearchTweets(string query)
{
List<ITweet> tweets = new List<ITweet>();
TwitterCredentials.ExecuteOperationWithCredentials(Credentials, () =>
{
var searchParam = Search.GenerateSearchTweetParameter(query);
searchParam.Lang = Language.English;
searchParam.MaximumNumberOfResults = 100;
tweets = Search.SearchTweets(searchParam);
});
return tweets;
}
but the limitation here is that I can only get a max 100 tweets. I want to leverage the stream in a similar way and get a stream of tweets matching the tracks that I pass. I have tried this:
public void FilterStream(string query)
{
IFilteredStream tweets;
TwitterCredentials.ExecuteOperationWithCredentials(Credentials, () =>
{
var filteredStream = Tweetinvi.Stream.CreateFilteredStream();
filteredStream.AddTrack(query);
filteredStream.AddTrack("#" + query);
filteredStream.MatchingTweetReceived += (sender, args) => { Debug.WriteLine(args.Tweet.Text); };
filteredStream.StartStreamMatchingAllConditions();
});
}
but problem is it seems to run in an infinite loop and i'm unsure where to stop or limit the number of tweets I received from the stream to make it stop. The library's documentation is quite unclear and I have been unable to achieve the behavior I am seeking. I'm sure I am on the right route, just unsure how to stop the stream and store all the tweets I've received in a List<ITweet> construct.
To stop the stream use:
filteredStream.StopStream();
You can keep a count of the tweets globally then stop when the number of tweets you need is received.
I had the same problem and ended up doing this:
var task = filteredStream.StartStreamMatchingAllConditionsAsync();
while (true)
{
Thread.Sleep(500);
if (task.IsCanceled || task.IsCompleted || task.IsFaulted)
{
break;
}
if (ShouldStop)
{
filteredStream.StopStream();
break;
}
}
task.Wait();
StartStreamMatchingAllConditionsAsync() will start the task and return immediately, then the endless while loop will check for a condition to stop the stream and break the loop.
ShouldStop is a static boolean variable that is flipped to True when the code should stop streaming from twitter.
Then we Wait() for the stream task to finish.
That code smells but it seems to work! I'd be curious to see if someone comes up with a better implementation...
You should try to put a condition inside the MatchingTweetReceived method like this :
public void FilterStream(string query)
{
IFilteredStream tweets;
TwitterCredentials.ExecuteOperationWithCredentials(Credentials, () =>
{
var filteredStream = Tweetinvi.Stream.CreateFilteredStream();
filteredStream.AddTrack(query);
filteredStream.AddTrack("#" + query);
filteredStream.MatchingTweetReceived += (sender, args) => {
Debug.WriteLine(args.Tweet.Text);
if (condition)
stream.StopStream();
};
filteredStream.StartStreamMatchingAllConditions();
});
}
This worked fine for me.
I know it's an old question, but I've just done this:
public void Start()
{
_stream = Stream.CreateFilteredStream(_creds);
// Do whatever stream setup you want to do
Task.Factory.StartNew(() => {
Debug.WriteLine("Thread started");
_stream.StartStreamMatchingAllConditions();
Debug.WriteLine("Thread stopped");
});
}
public void Stop()
{
_stream.StopStream();
}
I want to call two or more methods after another. When One function execution get's completed I need to call another method. Basically I am trying to implement the backup functionality in my app. I am developing Windows Phone app which take backup of contacts, images, video. I have created the method for each of these. When backup of contacts is completed then I want to call images method. I have created different functions for these. How can I call these function one after another?
I have tried something like this.
// Constructor
public MainPage()
{
InitializeComponent();
UploadImagesThread = new Thread(UploadImages);
UploadContactsThread = new Thread(UploadContacts);
// Sample code to localize the ApplicationBar
//BuildLocalizedApplicationBar();
}
On Upload button click
if (chkContacts.IsChecked.Value)
{
Dispatcher.BeginInvoke(() =>
{
SystemTray.ProgressIndicator.Text = "Searching contacts...";
});
UploadContactsThread.Start();
}
if (chkImages.IsChecked.Value)
{
Dispatcher.BeginInvoke(() =>
{
SystemTray.ProgressIndicator.Text = "Compressing images...";
});
UploadImagesThread.Start();
}
But It will not help me. How do I emplement? My UploadContact method has Async method call like this
Contacts objContacts = new Contacts();
objContacts.SearchCompleted += new EventHandler<ContactsSearchEventArgs>(objContacts_SearchCompleted);
objContacts.SearchAsync(string.Empty, FilterKind.None, null);
Use Task.ContinueWith() to chain up the calls.
You can have a look here: http://msdn.microsoft.com/de-de/library/dd321405(v=vs.110).aspx.
Applied to your problem, this should do the trick:
Task.Factory.StartNew(UploadImages).ContinueWith(UploadContacts);
Try using Tasks, which will let you give you a thread pool for free. You can't do this in a constructor but you could override you OnNavigatedTo Mehtod:
protected override async void OnNavigatedTo(NavigationEventArgs e)
{
base.OnNavigatedTo(e);
await Task.Run(() => { UploadImages(); });
await Task.Run(() => { UploadContacts(); });
}
The await will ensure that the contacts will start uploading after your images are completed. You could also check out background uploading which means your app will not have to run while the action is completing.
Your description of the problem (call one method after another?) is not very clear, but looking at your code, I suppose you want to wait for the UploadImagesThread thread to complete before starting the UploadContactsThread.
Use tasks and the async/await keywords like this:
private async void OnButtonClick(object sender, ...)
{
if (chkContacts.IsChecked.Value)
{
SystemTray.ProgressIndicator.Text = "Searching contacts...";
await Task.Run(() => UploadImages());
}
if (chkImages.IsChecked.Value)
{
SystemTray.ProgressIndicator.Text = "Compressing images...";
await Task.Run(() => UploadContacts());
}
}
Note: assuming your 2nd block of code is running on the UI thread, you shouldn't need to use BeginInvoke.
Edit
In response to your recent changes: you need a redesign. Try this:
private async void OnButtonClick(object sender, ...)
{
bool uploadContacts = chkContacts.IsChecked.Value;
bool uploadImages = chkImages.IsChecked.Value;
//use this if the continuation runs on the UI thread
Action continuation = async () => {
if(uploadImages) {
SystemTray.ProgressIndicator.Text = "Compressing images...";
await Task.Run(() => UploadImages());
}
};
//OR this if it doesn't
Action continuation = () => {
if(uploadImages) {
Dispatcher.BeginInvoke(() => SystemTray.ProgressIndicator.Text = "Compressing images...");
UploadImages();
}
};
if (uploadContacts)
{
SystemTray.ProgressIndicator.Text = "Searching contacts...";
UploadContacts(continuation);
}
}
private void UploadContacts(Action continuation)
{
Contacts objContacts = new Contacts();
//when the search has finished, trigger your event handler AND the continuation task, which will upload the images
objContacts.SearchCompleted += objContacts_SearchCompleted;
objContacts.SearchCompleted += (sender, args) => continuation();
objContacts.SearchAsync(string.Empty, FilterKind.None, null);
}
Try something like that :
bool backContact = chkContacts.IsChecked.Value;
bool backImages = chkImages.IsChecked.Value;
Task.Factory.StartNew(() =>
{
if (backContact) {
Dispatcher.BeginInvoke(() =>
{
SystemTray.ProgressIndicator.Text = "Searching contacts...";
});
UploadContacts;
});
}).ContinueWith(() =>
{
if (backImages) {
Dispatcher.BeginInvoke(() =>
{
SystemTray.ProgressIndicator.Text = "Compressing images...";
});
UploadImages;
}
}