【问题标题】:How to call one async method from another one in ASP.NET Web API application如何在 ASP.NET Web API 应用程序中从另一个异步方法调用一个异步方法
【发布时间】:2017-12-12 22:56:57
【问题描述】:

所以我在 ASP.NET Web API 应用程序中有以下异步方法的程序流程:API 控制器调用异步方法(我们称之为 asyncMethod1),它需要第二个异步的东西方法(我们称之为 asyncMethod2)。 API 控制器实际上并不需要知道 asyncMethod1 何时完成,尽管它会很好地为用户提供反馈。另一方面,asyncMethod1 确实需要知道 asyncMethod2 何时完成。

以下是相关代码:

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 是我正在使用的自定义 API 中的一个类,其中方法 DoAsyncWork1DoAsyncWork2 都是异步的。

当上面的代码运行时,我在方法WorkMethod中得到以下异常,行Task.Run(async () =&gt; await this.asyncMethod1(testObject)).Wait();:

"Message":"发生错误。","ExceptionMessage":"发生一个或多个错误。","ExceptionType":"System.AggregateException","StackTrace":" at
System.Threading.Tasks.Task.ThrowIfExceptional(Boolean includeTaskCanceledExceptions)
在 System.Threading.Tasks.Task.Wait(Int32 毫秒超时,CancellationToken cancelToken)
在 System.Threading.Tasks.Task.Wait()
在 MyController.cs 中的 MyController.WorkMethod(Test testObject):第 9 行
在 lambda_method(Closure , Object , Object[] )
在 System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.c__DisplayClass10。 b__9(Object instance, Object[] methodParameters)
在 System.Web.Http.Controllers.ReflectedHttpActionDescriptor.ActionExecutor.Execute(Object instance, Object[] arguments)
在 System.Web.Http.Controllers.ReflectedHttpActionDescriptor。 ExecuteAsync(HttpControllerContext controllerContext, IDictionary`2 arguments, CancellationToken cancelToken)

输出如下:

WorkMethod - 调用 asyncMethod1 之前
asyncMethod1 - 调用 asyncMethod2 之前
asyncMethod2 - 调用 DoAsyncWork2 之前
asyncMethod2 - 向调用者返回数据
asyncMethod1 - 调用 asyncMethod2 之后

如果,在 WorkMethod 方法中我改变了这个:

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

到这里:

var task = this.asyncMethod1(asyncMethod1);

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

task.wait();

我没有遇到异常,但程序流永远不会到达 asyncMethod1 中的点,它对从 asyncMethod2 返回的响应执行一些工作。另外,输出如下:

WorkMethod - 调用 asyncMethod1 之前
asyncMethod1 - 调用 asyncMethod2 之前
asyncMethod2 - 调用 DoAsyncWork2 之前
WorkMethod - 调用 asyncMethod1 之后

这里有什么问题?什么是我想要实现的正确方法,即从 API 控制器调用异步方法,然后从第一个异步方法调用第二个异步方法?

【问题讨论】:

  • 您永远不应该阻止使用Wait().Result 的异步调用。为什么WorkMethod 不是异步的? Task.Run()会做哪些工作?如果工作特别受 IO 限制,那你就是在浪费线程。
  • @Crowcoder 我对此没有特别好的答案,除了以下我发现非异步方法调用异步方法的示例。调用异步方法的方法是否也必须是异步的?在链上的某个时刻,你必须有一个非异步方法,对吧?!它们不可能都是异步的。至于你的第二个问题,正在做的工作是 Task.Run() 是调用 asyncMethod1。
  • 在 web api 中,保持一切异步应该很容易,因为它不会影响调用它的客户端。如果你不能让它全部异步,那么最好让它全部同步,因为将两者混合会导致资源使用效率低下和死锁风险。我看到它调用asyncMethod1,但显然它没有充实,所以我不知道你将在该方法中实际做什么。
  • @Crowcoder 它不可能全部同步,因为我依赖于另一个 API,其方法是异步的。因此,根据您所说的,它必须全部是异步的。你是对的,WorkMethod 还没有完成,但我唯一需要做的就是让用户知道操作是否成功。
  • 请记住,您必须等待任何呼叫,您不能执行“一劳永逸”并仍将任何结果报告给用户。

标签: c# asp.net asp.net-web-api async-await


【解决方案1】:

你在评论中说:

在链上的某个点,你必须有一个非异步方法, 对吧?!

答案是否定的。一切都可以是异步的。因此,您可以将此作为您的操作:

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();
}

然后这样做会使该方法毫无意义。您可以使您的asyncMethod1 成为实际的操作方法。如果你愿意。

也就是说,您得到的异常是因为实际上存在异常。但是因为你没有awaiting 结果,所以它会作为AggregateException 抛出,而InnerExceptions 集合中的真正异常。一旦你await 任务,你会看到真正的异常抛出。

【讨论】:

  • 你是对的。一旦我能够看到真正的异常被抛出,我发现问题不在于我的代码,而在于类 CustomClass 所在的自定义 API。一旦解决了这个问题,事情就开始正常了。
猜你喜欢
  • 2014-09-13
  • 2017-04-27
  • 1970-01-01
  • 1970-01-01
  • 2012-02-09
  • 2020-03-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多