混乱使用async / await web调用

时间:2015-05-27 04:00:08

标签: c# .net asynchronous async-await

我有一个Web API服务,用于检索和更新一组特定的数据(MyDataSet对象),在执行以下事件时,我在使用async / await时遇到了一些困惑:

  • 使用新值更新MyDataSet
  • 获取MyDataSet(但仅在新值更新后)

在我的客户中,我有类似以下的内容:

Harmony.cs

private async Task<string> GetDataSet(string request)
{
  using(var httpClient = new HttpClient())
  {
    httpClient.baseAddress = theBaseAddress;
    HttpResponseMessage response = await httpClient.GetAsync(request);
    response.EnsureSuccessStatusCode();
    return response.Content.ReadAsStringAsync().Result;
  }
}

private async Task PostDataSet<T>(string request, T data)
{
  using (var httpClient = new HttpClient())
  {
    client.BaseAddress = new Uri(theBaseAddress);
    HttpResponseMessage response = await client.PostAsJsonAsync<T>(request, data);
    response.EnsureSuccessStatusCode();
  }
}

internal MyDataSet GetMyDataSetById(int id)
{
  string request = String.Format("api/MyDataSet/GetById/{0}", id);
  return JsonConvert.DeserializeObject<MyDataSet>(GetDataSet(request).Result);
}

internal void UpdateDataSet(MyDataSet data)
{
  PostDataSet("api/MyDataSet/Update", data);
}

HarmonyScheduler.cs

internal void ScheduleDataSet()
{
  MyDataSet data = ...
  harmony.UpdateDataSet(data);
  MyDataSet data2 = harmony.GetMyDataSetById(data.Id);
}

Harmony.cs UpdateDataSet中存在编译器警告,因为没有等待调用,并且在调用完成之前将继续执行。这会影响程序执行,因为在更新发生之前正在检索data2。

如果我要使UpdateDataSet异步并添加等待它,那么它只是将堆栈中的内容移动到一个级别,现在HarmonyScheduler会收到关于不等待的警告。

如何在检索data2之前等待更新完成,以便在data2对象中获得更新的值?

3 个答案:

答案 0 :(得分:2)

  

如何在检索data2之前等待更新完成,   这样我将在data2对象中获得更新的值?

我看到许多人不理解的事实是,使用带有async-await的TAP会像瘟疫一样感染您的代码。

我的意思是什么? 使用TAP会导致异步一直冒泡到调用堆栈的顶部,这就是为什么他们说async方法“一直”。这是使用该模式的建议。通常,这意味着如果要引入异步API,则必须将其与单独的同步API一起提供。大多数人试图在两者之间进行混合和匹配,但这会导致whole lot of trouble(以及许多SO问题)。

为了让事情正确,您必须将UpdateDataSetGetMyDataSetById变为异步。结果应如下所示:

private readonly HttpClient httpClient = new HttpClient();

private async Task<string> GetDataSetAsync(string request)
{
    httpClient.BaseAddress = theBaseAddress;

    HttpResponseMessage response = await httpClient.GetAsync(request);
    response.EnsureSuccessStatusCode();

    return await response.Content.ReadAsStringAsync();
}

private async Task PostDataSetAsync<T>(string request, T data)
{
    client.BaseAddress = new Uri(theBaseAddress);
    HttpResponseMessage response = await client.PostAsJsonAsync<T>(request, data);
    response.EnsureSuccessStatusCode(); 
}

internal async Task<MyDataSet> GetMyDataSetByIdAsync(int id)
{
    string request = String.Format("api/MyDataSet/GetById/{0}", id);
    return JsonConvert.DeserializeObject<MyDataSet>(await GetDataSetAsync(request));
}

internal Task UpdateDataSetAsync(MyDataSet data)
{
    return PostDataSetAsync("api/MyDataSet/Update", data);
}

注意 - HttpClientmeant to be reused而不是一次性的单个调用对象。我将封装为类级别字段并重用它。 如果要公开同步API,请使用公开同步API的HTTP库(例如WebClient)来执行此操作。

答案 1 :(得分:0)

在您需要结果之前,请务必等待任务。 如果任务在其正文中包含await,那么等待任务总是更好。 警告:请勿使用.Wait().Result

如果按照Control Flow in Async Programs所述进行控制流程,您会更好地理解这个概念。

因此,在您的情况下,我会使所有函数访问异步方法GetDataSet(string request)PostDataSet<T>(string request, T data),返回类型为Task 等待

答案 2 :(得分:-1)

由于您似乎不期望从PostDataSet函数返回任何结果,您可以等待它完成。

PostDataSet("api/MyDataSet/Update", data).Wait();