Task async and continuewith - c#

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.

Related

C# async await not working winforms ui freeze

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.

Why is event NOT waiting on callback to finish?

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.

How can I convert a function with a ref paramter to an async function?

I have the following function which I want to convert to an async / non locking function.
Here is the function in its currenc form:
private static void BlockForResponse(ref bool localFlag)
{
int count = 0;
while (!localFlag)
{
Thread.Sleep(200);
if (count++ > 50) // 200 * 50 = 10 seconds
{
//timeout
throw new TimeOutException();
}
}
}
here is my attempt:
private static async Task BlockForResponse(ref bool localFlag)
{
int count = 0;
while (!localFlag)
{
await Task.Delay(200);
if (count++ > 50) // 200 * 50 = 10 seconds
{
//timeout
throw new TimeOutException();
}
}
}
however I get a compile error saying that async functions cant have ref or out parameters. However this is the core functionality of the function.
Is it possible to convert it to an async function?
Explanation of code:
I must admit this is an odd piece of code, let me try an explain what its trying to do:
so there is a 3rd party dll which I need to use. Which provides me with services, I sadly have no control over this dll.
The way it works,
I call a command in the dll providing it a callback function which it calls once it has finished the task.
I can only move on to what I want to do once I have the result from that call. hence the need fro this function.
I make the call to the dll, providing it with a call back function:
private bool _commandFlag = false;
private bool _commandResponse;
public async Task ExecuteCommand(string userId, string deviceId)
{
var link = await LinkProviderAsync.GetDeviceLinkAsync(deviceId, userId);
try
{
//execute command
if (link.Command(Commands.ConnectToDevice, CallBackFunction))
{
BlockForResponse(ref _commandFlag);
return; //Received a response
}
else
{ //Timeout Error
throw new ConnectionErrorException();
}
}
catch (Exception e)
{
throw e;
}
}
private void CallBackFunction(bool result)
{
_commandResponse = result;
_commandFlag = true;
}
The way it works, I call a command in the dll providing it a callback function which it calls once it has finished the task.
Then what you really want is to use TaskCompletionSource<T> to create a TAP method, something similar to this.
public static Task<bool> CommandAsync(this Link link, Commands command)
{
var tcs = new TaskCompletionSource<bool>();
if (!link.Command(command, result => tcs.TrySetResult(result)))
tcs.TrySetException(new ConnectionErrorException());
return tcs.Task;
}
With this extension method in place, your calling code is much cleaner:
public async Task ExecuteCommand(string userId, string deviceId)
{
var link = await LinkProviderAsync.GetDeviceLinkAsync(deviceId, userId);
var commandResponse = await link.CommandAsync(Commands.ConnectToDevice);
}
The problem with combining async and ref is that code inside an async function can run even after the method returns. So, if you did something like:
async Task BlockForResponseAsync(ref bool localFlag)
{
while (!localFlag)
{
...
}
}
void SomeMethod()
{
bool flag = false;
BlockForResponseAsync(ref flag); // note: no await here
}
Then the local variable flag would stop existing after SomeMethod() returned, but BlockForResponseAsync(), which has a reference to that variable, could still be executing. This is why the above code won't compile.
Basically, what you need is a closure, and in C#, ref doesn't create closures, but lambdas do. This means you can write your method like this:
async Task BlockForResponseAsync(Func<bool> localFlagFunc)
{
while (!localFlagFunc())
{
...
}
}
And use it like this:
bool flag = false;
var task = BlockForResponseAsync(() => flag);
// other code here
flag = true;
await task; // to make sure BlockForResponseAsync() completed successfully
This way also indicates your intention better. ref usually means something like: "give me a variable with some value, and I will change that value", which is not what you want here. On the other hand Func<T> means "give me something that I can use retrieve some value, potentially multiple times".

Call two or more functions one after another

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;
}
}

When exactly is a CallbackMethod invoked on an Async operation?

I am adding some data to a list and it might take a while to do this. Therefore i perform this action asynchronously. I do it like this:
ScanDelegate worker = StartScan;
AsyncCallback completedCallback = ScanCompletedCallback;
lock (_sync)
{
var async = AsyncOperationManager.CreateOperation(null);
worker.BeginInvoke(completedCallback, async);
}
The information added in StartScan() is correctly added to the list. When the scan is complete, i want to perform a different scan, depending on the data in the list. So i start the different scan in the ScanCompletedCallback() method. But at this point, my list is empty. Im guessing that this is because the callback method is invoked when the worker has been started, and not when it returns.
Is this true?
And if it is, how can i know when my worker has completed its tasks?
Edit: I can't get my head around this. It doesn't make sense. I came to think of the list i am adding to. I couldn't just add to it, i had to wrap it in a Dispatcher thread. This must be the problem, right? Is there a way i can make my Async method StartScan() wait for this Dispatcher ?
Thanks in advance!
StartScan()
private void StartScan()
{
//For testing
for (int t = 0; t < 1; t++)
{
var app2 = Application.Current;
if (app2 != null)
{
app2.Dispatcher.BeginInvoke(DispatcherPriority.Background,
new DispatcherOperationCallback(AddComputerToList),
new ComputerModel()
{
HostName = "Andreas-PC",
IpAddress = "192.168.1.99",
ActiveDirectoryPath = "ikke i AD",
Name = "Andreas-PC",
OperatingSystem = "Microsoft Windows 7 Enterprise"
});
}
}
}
ScanCompletedCallback()
private void ScanCompletedCallback(IAsyncResult ar)
{
var worker = (ScanDelegate)((AsyncResult)ar).AsyncDelegate;
var async = (AsyncOperation)ar.AsyncState;
worker.EndInvoke(ar);
lock (_sync)
{
IsScanning = false;
}
var completedArgs = new AsyncCompletedEventArgs(null, false, null);
async.PostOperationCompleted(e => OnScanCompleted((AsyncCompletedEventArgs)e), completedArgs);
}
AddComputerToList()
private object AddComputerToList(Object objComputer)
{
var computer = objComputer as ComputerModel;
if (computer != null)
{
ComputersList.Add(computer);
OnPropertyChanged("ComputersList");
}
return null;
}
As a direct answer to your question the callback will occur on completion of ScanCompletedCallback.
If you have a list that you believe should have some data in it at this point then there appears to be something else wrong, posting some more code may help with this.

Categories

Resources