2

So I have the following program flow with async methods in an ASP.NET Web API application: an API controller calls an async method (let's call it asyncMethod1), which needs something from a second async method (let's call it asyncMethod2). The API controller doesn't really need to know when asyncMethod1 finishes, though it would be nice to in order to give the user feedback. asyncMethod1, on the hand, does need to know when asyncMethod2 finishes.

Here is the relevant code:

public class MyController : ApiController
{
    private CustomClass customClass;

    public IHttpActionResult WorkMethod(TestObject testObject)
    {
        Debug.WriteLine("WorkMethod - before calling asyncMethod1");

        Task.Run(async () => await this.asyncMethod1(testObject)).Wait(); // ERROR HERE

        Debug.WriteLine("WorkMethod - after calling asyncMethod1");

        return Ok();
    }

    private async Task asyncMethod1(TestObject testObject)
    {
        Debug.WriteLine("asyncMethod1 - before calling asyncMethod2");

        Dictionary<string, string> x = await this.asyncMethod2(testObject);

        Debug.WriteLine("asyncMethod1 - after calling asyncMethod2");

        try
        {
            using (Task<IList<TestResponse>> testTask = customClass.DoAsyncWork1(x))
            {
                IList<TestResponse> response = await testTask;

                // Do something with response
                Debug.WriteLine("Transform 'response'");
            }
        }
        catch (Exception ex)
        {
            throw new Exception(" Exception ", ex);
        }
    }

    private async Task<Dictionary<string, string>> asyncMethod2(TestObject testObject)
    {
        try
        {
            Debug.WriteLine("asyncMethod2 - before calling DoAsyncWork2");

            using (Task<IList<SomeData>> workTask = customClass.DoAsyncWork2(testObject.x))
            {
                IList<SomeData> data = await workTask ;

                var output = new Dictionary<string, string>();
                output = data.values;

                Debug.WriteLine("asyncMethod2 - returning data to caller");

                return output;
            }
        }
        catch (Exception ex)
        {
            throw new Exception(" Exception ", ex);
        }
    }
}

CustomClass is a class in a custom API I am using, wherein methods DoAsyncWork1 and DoAsyncWork2 are both async.

When the code above runs, I get the following exception in method WorkMethod, line Task.Run(async () => await this.asyncMethod1(testObject)).Wait();:

"Message":"An error has occurred.","ExceptionMessage":"One or more errors occurred.","ExceptionType":"System.AggregateException","StackTrace":" at
System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
at System.Threading.Tasks.Task.Wait(Int32 millisecondsTimeout, CancellationToken cancellationToken)
at System.Threading.Tasks.Task.Wait()
at MyController.WorkMethod(Test testObject) in MyController.cs:line 9
at lambda_method(Closure , Object , Object[] )
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.<>c__DisplayClass10.b__9(Object instance, Object[] methodParameters)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
at System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancellationToken)

And the output is the following:

WorkMethod - before calling asyncMethod1
asyncMethod1 - before calling asyncMethod2
asyncMethod2 - before calling DoAsyncWork2
asyncMethod2 - returning data to caller
asyncMethod1 - after calling asyncMethod2

If, in method WorkMethod I change this:

Task.Run(async () => await this.asyncMethod1(asyncMethod1)).Wait();

to this:

var task = this.asyncMethod1(asyncMethod1);

Debug.WriteLine("WorkMethod - after calling asyncMethod1");

task.wait();

I don't get an exception, but the program flow never gets to the point in asyncMethod1 where it does some work over the response returned from asyncMethod2. In addition, the output is the following:

WorkMethod - before calling asyncMethod1
asyncMethod1 - before calling asyncMethod2
asyncMethod2 - before calling DoAsyncWork2
WorkMethod - after calling asyncMethod1

What is the problem here? What is the correct way to do what I am trying to achieve, i.e. call an async method from an API controller and then call a second async method from the first one?

5
  • You should never block on async calls with Wait() or .Result. Why isn't WorkMethod async? What work will be done in Task.Run()? You are wasting a thread if the work is IO bound especially. Commented Dec 12, 2017 at 14:28
  • @Crowcoder I don't have a particularly good answer to that, other than following examples I found where a non-async method calls an async one. Is it mandatory for a method calling an async method to also be async? At some point up the chain, you have to have a non-async method, right?! They can't all be async. As for your second question, the work being done is Task.Run() is to call asyncMethod1. Commented Dec 12, 2017 at 14:38
  • In a web api it should be easy to keep everything async since it will not affect the clients that call it. If you cannot make it all async then it is better to make it all synchronous because mixing the two causes inefficient use of resources and risk of deadlock. I see it calls asyncMethod1 but clearly it is not fleshed out so I don't know what you are going to actually do in that method. Commented Dec 12, 2017 at 14:41
  • @Crowcoder It cannot be all synchronous because I depend on another API, whose methods are asynchronous. Therefore it has to be all async, according to what you are saying. You are right, WorkMethod is not finished yet, but the only thing I need to do that there is let the user know whether the operation succeeded or not. Commented Dec 12, 2017 at 14:45
  • Keep in mind you must await any calls, you cannot do a "fire and forget" and still report any result from it to the user. Commented Dec 12, 2017 at 14:50

1 Answer 1

5

You said in your comment:

At some point up the chain, you have to have a non-async method, right?!

The answer to that is no. Everything can be async. So you can have this as your action:

public async Tack<IHttpActionResult> WorkMethod(TestObject testObject)
{
    Debug.WriteLine("WorkMethod - before calling asyncMethod1");

    await this.asyncMethod1(testObject);

    Debug.WriteLine("WorkMethod - after calling asyncMethod1");

    return Ok();
}

Then doing that kind of makes that method pointless. You can make your asyncMethod1 the actual action method. If you want.

That said, the exception you were getting is because there is actually an exception. But because you weren't awaiting the result, it gets thrown as an AggregateException with the real exception inside the InnerExceptions collection. Once you await the task, you'll see the real exception thrown.

Sign up to request clarification or add additional context in comments.

1 Comment

You are correct. Once I was able to see the real exception being thrown, I figured out the problem was not in my code, but in the custom API where class CustomClass lives. Once that was fixed, things started working fine.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.