Home > Net >  Converting WCF Web service calls from callback-based async method to awaitable task
Converting WCF Web service calls from callback-based async method to awaitable task

Time:05-20

I am converting over an old Silverlight application that used callback-based WCF Web Services over to OpenSilver that is using awaitable task based WCF Web Services. I am trying to figure out how to handle the error conditions. Here is the callback-based code:

private void GetNextImage()
{
    var cmc = ServiceFactories.CreateCartManager();
    cmc.getSlideImageCompleted  = (s, e) =>
    {
        cmc_getSlideImageCompleted(s, e);
    };

    var lastTime = SystemSettings.GetInstance().SlideShowData.LastImageTime;
    cmc.getSlideImageAsync(string.IsNullOrEmpty(lastTime) ? null : lastTime);
}

void cmc_getSlideImageCompleted(object sender, getSlideImageCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        GetNextImage();
    }
    else if (e.Error != null)
    {
        var errMsg = new ErrorWindow("Error while trying to get next image in slide show:", msg);
        errMsg.Show();
    }
    else if (e.Result == null)
    {
        // There are no images in the slide show right now.
    }
    else
    {
        // we have an image!!!!
        var imageData = e.Result.imageData;
        // <the rest of the code>
    }
}       

I know that GetNextImage() should look like this:

private async Task GetNextImage()
{
    var cmc = ServiceFactories.CreateCartManager();

    var lastTime = SystemSettings.GetInstance().SlideShowData.LastImageTime;
    var result = await cmc.getSlideImageAsync(string.IsNullOrEmpty(lastTime) ? null : lastTime);
    cmc_getSlideImageCompleted(result);
}

void cmc_getSlideImageCompleted(getSlideImageResponse e)
{
    ...
}

The question is, what happened to e.Cancelled, e.Error, and e.Result? How do I account for that lower level errors now?

CodePudding user response:

What happened to e.Cancelled, e.Error, and e.Result?

e.Cancelled

If you have an async method (in your case the cmc.getSlideImageAsync) then this can be cancelled through a CancellationToken. Inside this method if you repeatedly check where the cancellation has been requested or not (via the ThrowIfCancellationRequested) then it will throw a OperationCanceledException (or a derived class).

So, the equivalent of e.Cancelled is this:

getSlideImageResponse response;
try
{
   response = await cmc.getSlideImageAsync(..., cancellationToken);
}
catch(OperationCanceledException ex)
{
   //if(e.Cancelled) logic goes here
}

e.Error

If your async method fails for whatever reason then it will populate the underlying Task's Exception property.

Task<getSlideImageResponse> getTask = cmc.getSlideImageAsync(...);
getTask.Wait(); //BAD PRACTICE, JUST FOR DEMONSTRATION PURPOSES
if(getTask.Exception != null)
{
   //if(e.Error != null) logic goes here
}

The above code is suboptimal since the .Wait is a blocking call and can cause deadlock. The recommended approach is to use await. This operator can retrieve the .Exception property from the Task and can throw it again:

getSlideImageResponse response;
try
{
   response = await cmc.getSlideImageAsync(...);
}
catch(Exception ex)
{
   //if(e.Error != null) logic goes here
}

e.Result

This property was populated only if the method was not cancelled or did not fail. The same is true here:

getSlideImageResponse response;
try
{
   response = await cmc.getSlideImageAsync(..., cancellationToken);
}
catch(OperationCanceledException ocex)
{
   //if(e.Cancelled) logic goes here
}
catch(Exception ex)
{
   //if(e.Error != null) logic goes here
}

//if(e.Result != null) logic goes here
  • Related