1
\$\begingroup\$

Sometimes I need to call async code from a synchronous method. This leads to repetition in code to wait for the task to complete, then ensure that the expected exception is thrown instead of an aggregate exception (assuming there's only 1 exception returned, as is the case unless we're iterating through a list / something like that; in which case we preserve the AggregateException as that's then expected by the handling code).

To avoid repetition, would a helper function called and defined as below be appropriate? Does something like this already exist in the framework?

Example calling code:

bool MySyncMethod (bool throwException)
{
    var task = MyAsyncMethod(throwException);
    return task.WaitResultUnwrapException<bool>();
    //or even
    //return MyAsyncMethod(throwException).WaitResultUnwrapException<bool>();
}
async Task<bool> MyAsyncMethod (bool throwException)
{
    if (throwException)
        throw new ArgumentException(nameof(throwException));
    return await Task.Run(() => true);
}

Example extension method:

using System;
using System.Runtime.ExceptionServices;
using System.Threading.Tasks;

namespace MyCompany.Threading.Tasks
{
    public static class TaskExtensions
    {
        public static T WaitResultUnwrapException<T>(this Task<T> task)
        {
            if (task == null) throw new ArgumentNullException(nameof(task));
            try
            {
                task.Wait();
                return task.Result;
            }
            catch (AggregateException es)
            {
                if (es.InnerExceptions.Count == 1) {
                    var e = es.InnerExceptions[0];
                    ExceptionDispatchInfo.Capture(e).Throw();
                }
                throw;  
            }
        }
        public static void WaitResultUnwrapException(this Task task)
        {
            if (task == null) throw new ArgumentNullException(nameof(task));
            try
            {
                task.Wait();
            }
            catch (AggregateException es)
            {
                if (es.InnerExceptions.Count == 1) {
                    var e = es.InnerExceptions[0];
                    ExceptionDispatchInfo.Capture(e).Throw();
                }
                throw;  
            }
        }
    }
\$\endgroup\$
  • \$\begingroup\$ ps. After posting I just thought that we may be iterating a list / with catch logic defined to build an aggregate exception, but only have 1 exception returned; in which case the exception type would differ from expected... Therefore I'd likely change the above to add a preserveAggegateException parameter to avoid unwrapping aggregates where they're expected. if (!preserveAggegateException && es.InnerExceptions.Count == 1) \$\endgroup\$ – JohnLBevan Nov 26 '18 at 17:20
  • 4
    \$\begingroup\$ mhmm... I think this is not necessary as task.GetAwaiter().GetResult() will also unwrap the exception with much less code, see this on Stack Overflow. \$\endgroup\$ – t3chb0t Nov 26 '18 at 17:25
  • \$\begingroup\$ Thanks; I'd not seen that approach; definitely makes sense / covers pretty much exactly what I've done above with less effort and the same caveat around moving you away from the benefits of being asynchronous. Thanks @t3chb0t. \$\endgroup\$ – JohnLBevan Nov 26 '18 at 17:31
2
\$\begingroup\$

Per the comment by @t3chb0t, all I needed was this:

task.GetAwaiter().GetResult()

In context:

bool MySyncMethod (bool throwException)
{
    return MyAsyncMethod(throwException).GetAwaiter().GetResult(); //this is the only line that's changed
}
async Task<bool> MyAsyncMethod (bool throwException)
{
    if (throwException)
        throw new ArgumentException(nameof(throwException));
    return await Task.Run(() => true);
}
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.