Перевод исключений для void Tasks с параллельной библиотекой задач a la await - PullRequest
0 голосов
/ 13 января 2012

Мне нужно перевести исключение, исходящее от 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);
    }
}

Как бы вы реализовали WrapsApiAsync() без необходимостиC # 5?

1 Ответ

1 голос
/ 13 января 2012

Хорошо, теперь, когда я полностью понимаю, что вы ищете, вот все, что вам нужно сделать, чтобы построить эквивалент в 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 при создании продолжения. Это потому, что эта работа небольшая и трудоемкая, и вам не нужно тратить время на ожидание того, что планировщик просто выберет из пула потоков еще один поток, чтобы выполнить эту проверку.

...