I had to write an async method to contact with web service. This is my method in WebServiceHelper class:
public static Task<int> SignIn(string username, string password)
{
try
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
service.LoginCompleted += (object sender, WebService.LoginCompletedEventArgs e) =>
{
if (e.Error != null) tcs.SetResult(-1);
else
tcs.SetResult((int)e.Result);
};
service.LoginAsync(username, password);
return tcs.Task;
}
catch (Exception ex)
{
MessageBox.Show("Error: " + ex.Message);
return null;
}
}
Then I call it in a button clicked event like this:
private async void btLogIn_Click(object sender, RoutedEventArgs e)
{
try
{
int si = await WebServiceHelper .SignIn(tbUsername.Text, tbPassword.Text);
if (si != 0) MessageBox.Show("Signed in successfully!");
else MessageBox.Show("Couldn't sign in");
}
catch (Exception ex)
{
MessageBox.Show(ex.Message);
}
}
It worked fine on the first time I clicked the button, but when I did in again, an error came up: "InvalidOperationException, An attempt was made to transition a task to a final state when it had already completed."
I did some little search and found something here: Tasks seem to start automatically
I understand that I should do something to stop the process before start it again but I don't know exactly how and why. Can someone please explain this for me?
I tried to use the method TrySetResult() instead of SetResult() and it worked!
I suspect the problem is that you're not unregistering your event handler and every time you make a call to this method you're adding a new anonymous event handler to service.LoginCompleted
Try this
public static Task<int> SignIn( string username, string password )
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
EventHandler<WebService.LoginCompletedEventArgs> onLoginCompleted = null;
onLoginCompleted = ( object sender, WebService.LoginCompletedEventArgs e ) =>
{
service.LoginCompleted += onLoginCompleted;
if(e.Error != null)
{
tcs.SetResult( -1 );
}
else
{
tcs.SetResult( (int)e.Result );
}
};
service.LoginCompleted += onLoginCompleted;
service.LoginAsync( username, password );
return tcs.Task;
}
or perhaps this
public static Task<int> SignIn( string username, string password )
{
TaskCompletionSource<int> tcs = new TaskCompletionSource<int>();
EventHandler<WebService.LoginCompletedEventArgs> onLoginCompleted = ( object sender, WebService.LoginCompletedEventArgs e ) =>
{
if(e.Error != null)
{
tcs.SetResult( -1 );
}
else
{
tcs.SetResult( (int)e.Result );
}
};
service.LoginCompleted += onLoginCompleted;
tcs.Task.ContinueWith(task => service.LoginCompleted -= onLoginCompleted);
service.LoginAsync( username, password );
return tcs.Task;
}
As an aside, you should also remove that universal try/catch
around the method and return tcs.Task
in all situations.
If it is actually likely that service.LoginAsync( username, password )
could throw an exception then you should do this
...
try
{
service.LoginAsync( username, password );
}
catch(SomeParticularException ex)
{
tcs.SetException(ex);
}
return tcs.Task;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With