使用任务并行库转换void任务的异常等待

时间:2012-01-13 15:27:11

标签: .net c#-4.0 exception-handling task-parallel-library

我需要翻译来自Task<T>的异常,其方式与对正常同步代码执行以下操作相同:

try {
  client.Call();
} catch(FaultException ex) {
    if (ex.<Some check>)
        throw new Exception("translated");
}

但是,我想以异步方式执行此操作,即上面的Call实际上是Task CallAsync()

所以在C#5中,我的方法看起来像这样:

async Task CallAndTranslate()
{
    try{
        await client.CallAsync();
    } catch(FaultException ex) {
        if (ex.FaultCode ...)
            throw new Exception("translated");
    }
}

但我现在正在使用C#4。

所以我能做什么,因为我想要触发一个任务,但是要翻译(TPL)错误,然后再将整个事件公开为Task<T>

  • 最初来自WCF网络服务,但这里并不重要

编辑:一种更具体的说法:

public class TranslatingExceptions
{
    public Task ApiAsync() // The inner layer exposes it exactly this way
    {
        return Task.Factory.StartNew(()=>{ throw new Exception( "Argument Null" );});
    }

    public Task WrapsApiAsync() // this layer needs to expose it exactly this way
    {
        // async preview pseudocode for what I need to do                            
        try {
            await ApiAsync(  );
        } catch (Exception exception){
            if( exception.Message == "Argument Null"  )
                throw new ArgumentNullException();
        }
    }

    [Fact]
    public void Works()
    {
        var exception = Record.Exception( () => 
            WrapsApiAsync().Wait());
        Assert.IsType<ArgumentNullException>( exception.InnerException);
    }
}

如果不需要C#5,您将如何实施WrapsApiAsync()

1 个答案:

答案 0 :(得分:1)

好的,现在我已经完全明白你在寻找什么了,这就是你需要做的就是在4.0中构建等价物:

public class TranslatingExceptions
{
    public Task ApiAsync() // The inner layer exposes it exactly this way
    {
        return Task.Factory.StartNew(()=>{ throw new Exception( "Argument Null" );});
    }

    public Task WrapsApiAsync() // this layer needs to expose it exactly this way
    {
        // Grab the task that performs the "original" work
        Task apiAsyncTask = ApiAsync();

        // Hook a continuation to that task that will do the exception "translation"
        Task result = aspiAsync.ContinueWith(antecedent =>
        {
            // Check if the antecedent faulted, if so check what the exception's message was
            if ( antecedent.IsFaulted 
              && antecedent.Exception.InnerException.Message == "Argument Null" )
            {
                throw new ArgumentNullException();
            }
        },
        TaskContinuationOptions.ExecuteSynchronously);

        // Now we return the continuation Task from the wrapper method so that the caller of the wrapper method waits on that
        return result;
    }

    [Fact]
    public void Works()
    {
        var exception = Record.Exception(() => 
                                         WrapsApiAsync().Wait());

        Assert.IsType<ArgumentNullException>(exception.InnerException);
    }
}

这应该可以实现您的目标。需要注意的一点是,在创建延续时我使用TaskContinuationOptions.ExecuteSynchronously。这是因为这项工作很小而且很紧,你不想让调度程序等待从线程池中拾取整个其他线程只是为了进行这项检查。