将 WCF Web 服务调用从基于回调的异步方法转换为等待任务

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

我正在将使用基于回调的 WCF Web 服务的旧 Silverlight 应用程序转换为使用基于可等待任务的 WCF Web 服务的 OpenSilver。我想弄清楚如何处理错误情况。这是基于回调的代码:

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

我知道 GetNextImage() 应该是这样的:

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)
{
    ...
}

问题是,e.Cancelled、e.Error、e.Result发生了什么?我现在如何解释较低级别的错误?

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

e.Cancelled

如果您有异步方法(在您的情况下为 cmc.getSlideImageAsync),则可以通过 CancellationToken 取消。在此方法中,如果您反复检查请求取消的位置(通过 ThrowIfCancellationRequested),那么它将抛出一个 OperationCanceledException(或派生的 class)。

所以,e.Cancelled 的等价物是这样的:

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

e.Error

如果您的异步方法因任何原因失败,那么它将填充底层 TaskException 属性.

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

上面的代码不是最理想的,因为 .Wait 是一个阻塞调用,可能会导致死锁。推荐的方法是使用 await。此运算符可以从 Task 中检索 .Exception 属性 并可以再次抛出它:

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

e.Result

此 属性 仅在方法未取消或未失败时才会填充。这里也是一样:

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