Monday, July 25, 2016

Async callback is called but not executed sometimes

Leave a Comment

Silverlight project.

In my View code behind, I call a method in the View Model to get value.

 public MyViewModel ViewModel     {         get         {             if (this.viewModel == null)                 this.viewModel = new MyViewModel();             return this.viewModel;         }         set         {             if (this.viewModel != value)             {                 this.viewModel = value;             }         }     } 

However the async callback is not called. Sometimes it is called by delay. Thus I get the value as 1900-01-01 00:00:00.000 instead of the correct date time.

DateTime value; public void GetDateTime()     {         var proxy = new WcfClient();         proxy.GetCurrentDateTimeCompleted -= ProxyGetCurrentDateTimeCompleted;         proxy.GetCurrentDateTimeCompleted += ProxyGetCurrentDateTimeCompleted;         proxy.GetCurrentDateTimeAsync();     }      private void ProxyGetCurrentDateTimeCompleted(object sender, GetCurrentDateTimeCompletedEventArgs args)     {         try         {             value = args.Result; 

To call it in code behind.

this.viewModel.GetDateTime(); 

Update 1

Upon the comment, I added some explanation. The View has a checkbox. If I click it or unclick it, a message box with Yes/No button will pop up. Whatever you select Yes or No, I will record the date and time in the MessageBoxYesNoSelection_Closed event. The event method is still in the code behind.

The checkbox part code is:

if (clickedCheckBox != null) {     var msgBoxControl = new MessageBoxControl();     msgBoxControl.Closed -= MessageBoxYesNoSelection_Closed;     msgBoxControl.Closed += MessageBoxYesNoSelection_Closed;      if (clickedCheckBox.IsChecked == true)     {         var curDateTime = this.ViewModel.value;// 1900-01-01 00:00:00.000 

Inside MessageBoxYesNoSelection_Closed, we get the value.

this.ViewModel.GetDateTime();// WCF async call this.ViewModel.UpdateValue(value); // another WCF async call, need value from GetDateTime() method's return result. this.ViewModel.UpdateAnotherValue(value, user); // third WCF async call 

What I found is sometimes the async callbacks are not executed although they are called.

1 Answers

Answers 1

I guess that you are experiencing a race condition, as you are not awaiting the this.ViewModel.GetDateTime() call and therefore in most cases the callback that sets the value (ProxyGetCurrentDateTimeCompleted) has not been called before you are using the value in the next statement (this.ViewModel.UpdateValue(value)).

The easiest solution would be to use async & await as described here and here.

If you can't do that another solution would be to move the code that depends on the value into the callback method.

Update 1

There is a possibility to use async & await with Silverlight 4 and .NET Framework 4.0. See Microsoft.Bcl.Async for more information. However, this method requires at least Visual Studio 2012, so in case you can't switch to a newer Visual Studio version this won't work for you.

In that case the idea would be to move the code dependent on the value into the callback method. So at first you would only call GetDateTime():

this.ViewModel.GetDateTime(); // WCF async call 

Then you'll call the other methods in the callback method:

private void ProxyGetCurrentDateTimeCompleted(object sender, GetCurrentDateTimeCompletedEventArgs args) {     value = args.Result;     UpdateValue(value); // another WCF async call, need value from GetDateTime()     UpdateAnotherValue(value, user); // third WCF async call } 

Update 2

Following your last comment, I would recommend you to restructure your application so that you can use an approach as shown in the first update. Storing data in the code behind and the view model seems a bit odd. However, if you want a quick solution you could also wait for the callback to set the value manually. See the following code as example to get you started:

DateTime value; bool wcfCallInProgress; public void GetDateTime() {     var proxy = new WcfClient();     proxy.GetCurrentDateTimeCompleted -= ProxyGetCurrentDateTimeCompleted;     proxy.GetCurrentDateTimeCompleted += ProxyGetCurrentDateTimeCompleted;     wcfCallInProgress = true;     proxy.GetCurrentDateTimeAsync(); }  private void ProxyGetCurrentDateTimeCompleted(object sender, GetCurrentDateTimeCompletedEventArgs args) {     value = args.Result;     wcfCallInProgress = false; } 

Now you can add a loop which waits until the value has been set:

this.ViewModel.GetDateTime();// WCF async call  while (this.ViewModel.wcfCallInProgress) {     Thread.Sleep(10); }  this.ViewModel.UpdateValue(value); // another WCF async call, need value from GetDateTime() method's return result. this.ViewModel.UpdateAnotherValue(value, user); // third WCF async call 

If you use this approach you'll have to make sure that timeouts and exceptions won't be a problem. Otherwise you may end up with an infinite loop.

If You Enjoyed This, Take 5 Seconds To Share It

0 comments:

Post a Comment