将异步等待集成到同步方法中

时间:2017-12-12 14:28:01

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

我现在已经把头撞到墙上两天了,坦率地说,我对自己很生气,因为我似乎无法得到它

我在webapi上下文中。在此请求期间,我需要将一些数据发送到我们的其他系统之一,由于计算量大和多次数据库保存等原因,此系统返回速度很慢。我需要记录此操作的结果,无论它是否成功或不。但我不想等待它完成。

read我应该从上到下一直async await。如果我决定这样做的话,我将不得不转换很多方法,因为我已经深入了3或4种方法,我担心这种方法会更加突出。

我有什么选择?如果我一直向下等待异步,我怎么处理堆栈上方的方法,比如我的WebApi控制器?

这是我的代码,我试图尽可能地减少它。现在我在方法Task.Result()中使用PushResult()。我的理解是阻止异步?此代码的工作原理是请求被发送。但TestLog始终是最后的,而不是第一个。因此不是异步。

    //I'm in a public service and referenced twice
    private void MyEndProcess()
    {
        // other stuff

        _vendorPushService.PushResult(); // This could take a while and I have to wait for it!

        _logService.PostLog(LogType.TestLog, "Test");
    }

    //I'm referenced above and somewhere else in the code base
    public void PushResult()
    {   
        ExternalResultModel externalResultModel = _resultService.GetExternalResultModel();

        PushedResultModel pushedResult = new PushedResultModel();

        try
        {
            pushedResult = _vendorRequestService.PushResultAsync(externalResultModel).Result;
        }
        catch (Exception ex)
        {
            pushedResult.Success = false;
        }

        if (pushedResult.Success)
        {
            _logService.PostLog(LogType.SuccessLog, pushedResult.Message);
        }
        else
        {
            _logService.PostLog(LogType.FailedLog, pushedResult.Message);
        }
    }

    public async Task<PushedResultModel> PushResultAsync(ExternalResultModel externalResultModel)
    {
        // setup the requestMessage
        HttpResponseMessage responseMessage = await _httpRequestService
            .SendRequest(requestMessage)
            .ConfigureAwait(false);

        return new PushedResultModel
        {
            Success = responseMessage.IsSuccessStatusCode,
            Message = await responseMessage.Content.ReadAsStringAsync()
        };
    }

    public class HttpRequestService : IHttpRequestService
    {
        private readonly HttpClient _httpClient; 

        public HttpRequestService(IHttpClientAccessor httpClientAccessor)
        {
            _httpClient = httpClientAccessor.HttpClient;
        }

        public async Task<HttpResponseMessage> SendRequest(HttpRequestMessage requestMessage)
        {
            HttpResponseMessage httpResponseMessage = await _httpClient.SendAsync(requestMessage).ConfigureAwait(false);

            return httpResponseMessage;
        }
    }

2 个答案:

答案 0 :(得分:0)

你应该从上到下一直实现异步等待。

  

如果我一直等待async,我该如何处理堆栈上方的方法,比如我的WebApi控制器?

让你的控制器动作像这样异步:

[RoutePrefix("api")]
public class PresidentsController : ApiController
{
    [Route("presidents")]
    public async Task<IHttpActionResult> GetPresidents()
    {
        await Task.Delay(TimeSpan.FromSeconds(10)).ConfigureAwait(false);
        return Ok();
    }
}

这是实现异步方法的最简单方法。即使它会添加一些工作来将所有内容更改为异步,它将在未来受益,因为您将避免使用异步代码的许多问题。

如果你绝对必须在同步方法中使用异步方法,请将其阻塞在一个地方,如下所示:

public void MySyncMethod()
    {
        try
        {
            this.MyAsyncMethod().Wait();

        }
        catch (Exception exception)
        {
            //omited
        }
    }
private async Task MyAsyncMethod()
{
     await AsyncLogic().ConfigureAwait(false);
}

但我不推荐它。你应该只使用异步等待一直到控制器动作。

答案 1 :(得分:-1)

在您的评论中,您说您希望在后台处理任务,而不是让客户端调用您的API等待。要做到这一点,你真的不需要使用async / await。

试试这个:

private void MyEndProcess()
{
    // other stuff

    Task.Run(_vendorPushService.PushResult()).ConfigureAwait(false); //fire and forget

    _logService.PostLog(LogType.TestLog, "Test");
}

Task.Run将启动任务,ConfigureAwait(false)告诉它它不需要在我们当前所在的相同上下文中恢复(意味着上下文可以在任务之前关闭)已完成 - 即可以在不等待任务完成的情况下发回响应。

您将收到编译器警告,表示您没有等待Task.Run,但这就是您想要的。

请注意,当您执行此操作时,HttpContext.Current内将无法使用PushResult