简体   繁体   中英

How to simplify or wrap exceptions when rewriting synchronous code to use TPL

Given an implementation as follows:

public class SomeServiceWrapper
{
    public string GetSomeString()
    {
        try
        {
            //Do Something
        }
        catch (IOException e)
        {
            throw new ServiceWrapperException("Some Context", e);
        }
        catch (WebException e)
        {
            throw new ServiceWrapperException("Some Context", e);
        }
    }
}

The intention of the above is to enable the consumer of GetSomeString to only need to catch ServiceWrapperException .

Consider the following approach to extending this with a similar async behaviour:

public Task<string> GetSomeStringAsync()
{
    Task<string>.Factory doSomething = ...
    return doSomething.ContinueWith(x => 
    {
        if (x.IsFaulted)
        {
             if (x.Exception.InnerExceptions.Count() > 1)
             {
                 throw new AggregateException(x.Exception);
             }

             var firstException = x.Exception.InnerExceptions[0];
             if (typeof(firstException) == typeof(IOException)
                 || typeof(firstException) == typeof(WebException))
             {
                 throw new ServiceWrapperException("Some Context", firstException);
             }
        }

        return x.Result;
    }
} 

This synchronous approach to wrapping exceptions doesn't fit naturally with the asynchronous approach.

What could the author of SomeServiceWrapper do to simplify the exception handling code of any consumers so they only need to handle TradeLoaderException instead of both IOException and WebException ?

I made an extension method that pretty much does that. Usage:

public static Task<string> GetSomeStringAsync()
{
    var doSomething = Task.Factory.StartNew(() => "bar");
    return doSomething.WrapExceptions(typeof(IOException), typeof(WebException));
}
  1. You can just return the original task with the continuation.
  2. I would suggest changing ServiceWrapperException to hold more than one exception like AggregateException and then change the first part.

The Method:

public static Task<TResult> WrapExceptions<TResult>(this Task<TResult> task, params Type[] exceptionTypes)
{
    return task.ContinueWith(_ =>
    {
        if (_.Status == TaskStatus.RanToCompletion) return _.Result;

        if (_.Exception.InnerExceptions.Count > 1)
        {
            throw new AggregateException(_.Exception);
        }

        var innerException = _.Exception.InnerExceptions[0];
        if (exceptionTypes.Contains(innerException.GetType()))
        {
            throw new ServiceWrapperException("Some Context", innerException);
        }

        throw _.Exception;
    });
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM