0

I have a WPF application which sends data to a web application through POST requests. Currently this is done with PostAsync and that works. However, I see that certain requests finish earlier before another request finishes and that causes errors in the web application.

So I need to wait for until POST request is finished before sending the next request. I know I need to use Task, await and async for this, but I'm struggling in my specific situation.

The code below is the current code, without any async or await, because I'm unsure how to do this due to the chaining of objects.

First I have a button click event which calls an object for exporting the data:

private void exportButton_Click(object sender, RoutedEventArgs e)
{
  var dataExtractor = new DataExtractor();

  // First API call
  dataExtractor.ExportTestSteps(testblockId, testCase);

  // Second API call
  dataExtractor.ExportReportTestSteps(testCase, executionList);
}

These call a method in the DataExtractor class and after getting the right data these methods call the actual sending of the POST request:

public class DataExtractor
{
   public void ExportTestSteps(string testblockId, string testCaseUniqueId)
   {
     ...
     new QualityMonitorApi().StoreReportItem(content);
   }

   public void ExportReportTestSteps(string testCaseUniqueId, ExecutionList executionList)
   {
     ...
     new QualityMonitorApi().StoreReportItem(content);
   }     
}

And the QualityMonitorApi looks like this:

public class QualityMonitorApi
{
  private string baseUrl = "http://localhost:3000/api/v1";
  private static readonly HttpClient Client = new HttpClient();

  public void StoreReportItem(string content)
  {
    string url = baseUrl + "/data_extractor/store_report_item";

    var json = new StringContent(content, Encoding.UTF8, "application/json");
    Client.PostAsync(url, json);
  }
}

Due to the chaining of the classes I'm confused how to make sure API call 2 waits for API call 1 to finish?

John
  • 6,404
  • 14
  • 54
  • 106
  • Sorry for the confusion, this is the current code without the `async` and `await`, because I'm unsure how to do this. – John Mar 20 '18 at 12:11

2 Answers2

1

Use async/await

Your async method should look like this:

  public async Task StoreReportItem(string content)
  {
    string url = baseUrl + "/data_extractor/store_report_item";

    var json = new StringContent(content, Encoding.UTF8, "application/json");
    await Client.PostAsync(url, json);
  }

Next use async/await in every method:

   public async Task ExportReportTestSteps(string testCaseUniqueId, ExecutionList executionList)
   {
     ...
     await new QualityMonitorApi().StoreReportItem(content);
   }

and so on..

Evgeny Gorbovoy
  • 765
  • 3
  • 20
  • OK thanks, and what if e.g. `ExportReportTestSteps` returns a string instead of void, will it then be called as `public async Task ExportReportTestSteps()`? – John Mar 21 '18 at 06:32
0

You cannot do that way ? async void is okay, as long as it is an event handler method.

Also, you can store the objects that calls the API (i.e. DataExtractor) to avoid re-instantiating.

private async void exportButton_Click(object sender, RoutedEventArgs e)
{
  var dataExtractor = new DataExtractor();

  // First API call
  await dataExtractor.ExportTestStepsAsync(testblockId, testCase);

  // Second API call
  await dataExtractor.ExportReportTestStepsAsync(testCase, executionList);
}

public class DataExtractor
{
   public async Task ExportTestStepsAsync(string testblockId, string testCaseUniqueId)
   {
     ...
     await new QualityMonitorApi().StoreReportItemAsync(content);
   }

   public async Task ExportReportTestStepsAsync(string testCaseUniqueId, ExecutionList executionList)
   {
     ...
    await new QualityMonitorApi().StoreReportItemAsync(content);
   }     
}

public class QualityMonitorApi
{
  private string baseUrl = "http://localhost:3000/api/v1";
  private static readonly HttpClient Client = new HttpClient();

  public async Task StoreReportItemAsync(string content)
  {
    string url = baseUrl + "/data_extractor/store_report_item";

    var json = new StringContent(content, Encoding.UTF8, "application/json");
    await Client.PostAsync(url, json);
  }
}

See more here about async programmation: https://learn.microsoft.com/en-us/dotnet/csharp/async

Emy Blacksmith
  • 755
  • 1
  • 8
  • 27
  • "async void is okay, as long as it is an event handler method" - not only in this case. Actually in any case you don't care about time when its finished – Evgeny Gorbovoy Mar 20 '18 at 12:19
  • 2
    It isn't about time, but about catching exceptions. – Emy Blacksmith Mar 20 '18 at 12:21
  • not really. try to run async void Test(){throw new InvalidOperationException("This exception can be caught in caller method"); I can also show you example, when calling async Task does not receive exception from it. Exception are much more complicated in Tasks then just "async void" vs "async Task". async Task does not guarantee you'll get an exception. Read about TaskScheduler.UnobservedException – Evgeny Gorbovoy Mar 20 '18 at 12:25
  • 2
    "Figure 2 Exceptions from an Async Void Method Can’t Be Caught with Catch" from [Async/Await - Best Practices in Asynchronous Programming](https://msdn.microsoft.com/en-us/magazine/jj991977.aspx) (by Stephen C) @EugeneGorbovoy – Fildor Mar 20 '18 at 12:31
  • @Eugene Gorbovoy Catching exceptions outside the task. We can go far on this, talking about **deadlocks**, the use of ``ConfigureAwait(false)`` (because you talk about time while I was talking of something else) and so. – Emy Blacksmith Mar 20 '18 at 12:36
  • As I understood you don't care about exceptions from event handlers? Because if you do, you can use async void anywhere you want. Also nothing bad about cathcing exceptions outside of task as you have original stacktrace in original exception – Evgeny Gorbovoy Mar 20 '18 at 12:46