Rethrowing inner exception of an AggregateException

2.5k Views Asked by At

Let's say I have an Interface:

interface A {
    string Do();
}

and then I implement this interface in a class. The implementation requires some async operations. Something like the following:

class B : A {
    public string Do() {
        return Task1().Result;
    }

    private async Task<string> Task1() {
        var str = await Task2();

        return str + "task1";
    }

    private async Task<string> Task2() {
        using (WebClient client = new WebClient())
        {
            return System.Text.Encoding.UTF8.GetString(await client.DownloadDataTaskAsync(new Uri("http://test.com")));
        }
    }
}

What is the proper way to return, to the external calling code, the first exception that occurs in the async operations chain? Is the following a good approach?

public string Do() {
    try {
        return Task1().Result;
    } catch (AggregateException ex) {
        Exception inner = ex;
        while(inner.InnerException != null) {
            inner = inner.InnerException;
        }

        throw inner;
    }
}
1

There are 1 best solutions below

2
On BEST ANSWER

From your code, through the while, I think you want to throw the first exception in AggregateException To do that, you can use Flatten

Flattens an AggregateException instances into a single, new instance.

It helps to put the exceptions in "the same hierarchy", you can then simply call FirstOrDefault to get the first exception.

Supposed this code:

Task.Factory.StartNew(
        async () =>
        {
            await Task.Factory.StartNew(
                () => { throw new Exception("inner"); },
                TaskCreationOptions.AttachedToParent);

            throw new Exception("outer");
        }).Wait();
    }

The stucture of exceptions likes

AggregateException
    Exception: outer
    AggregateException
       Exception: inner

With Flatten, I can get inner

catch(AggregateException ex)
{
     Console.WriteLine(ex.Flatten().InnerExceptions.FirstOrDefault().Message);
}

but without Flatten, I get AggregateException, which isn't correct

catch(AggregateException ex)
{
     Console.WriteLine(ex.Flatten().InnerExceptions.FirstOrDefault().Message);
}

With your case, this line can help you get the first exception

ex.Flatten().InnerExceptions.FirstOrDefault().Message

You have also the method Handle, which help you handle the exception inside AggregateException

catch (AggregateException ex)
{
    ex.Handle(x =>
    {
        if (x is UnauthorizedAccessException)
        {
            //the exception you interested
            throw x;           
        }
        // Other exceptions will not be handled here.
        //some action i.e log
        return false;
    });
}