Search code examples
c#asynchronoushttpclient

C# Async and Sync functions confuse me


I have an app in which a button starts creating XMLs. In the end of each XML creation, the SendInvoice function sends it, receives the response and a function (ParseResponse) parses the responses and does the database operations needed.

The idea is that when all the XMLs are created and sent, the application must close. The problem is that I have lost control with async and the application seems to close before it actually finishes all the jobs. Also XMLs are sent before the previous have been processed.

The ParseResponse function is not asynchronous.

Here is the SendInvoice function.

Can you suggest any good practise?

Thank you in advance.

public async void SendInvoice(string body)
    {
        Cursor.Current = Cursors.WaitCursor;

        var client = new HttpClient();

        var queryString = HttpUtility.ParseQueryString(string.Empty);

        var uri = "https://xxxx.xxx/SendInvoices?" + queryString; 

        HttpResponseMessage response;

        // Request body
        byte[] byteData = Encoding.UTF8.GetBytes(body);

        using (var content = new ByteArrayContent(byteData))
        {
            content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
            response = await client.PostAsync(uri, content);
            string responsebody = await response.Content.ReadAsStringAsync();

            ParseResponse(response.ToString());
            ParseResponse(responsebody);

        }
    }

The rest of the code

private void button1_Click(object sender, EventArgs e)
{
 For
  {
     ......
      SendInvoice(xml)

  }
  System.Windows.Forms.Application.Exit();
}

Solution

  • Since you are calling the method from an Event Handler, this is a case where async void is acceptable, change your Button Click handler method signature to use async, I also added some ConfigureAwait(false) to async method calls - best-practice-to-call-configureawait-for-all-server-side-code and why-is-writing-configureawaitfalse-on-every-line-with-await-always-recommended:

    private async void button1_Click(object sender, EventArgs e)
    {
      //since you are using a for-loop, I'd suggest adding each Task
      //to a List and awaiting all Tasks to complete using .WhenAll()
      var tasks = new List<Task>();
      
      FOR
      {
         ......
          //await SendInvoice(xml).ConfigureAwait(false);
          tasks.Add(SendInvoice(xml));
    
      }
      await Task.WhenAll(tasks).ConfigureAwait(false);
      System.Windows.Forms.Application.Exit();
    
    }
    

    AND change your SendInvoice method signature to return a Task

    public async Task SendInvoice(string body)
    {
        Cursor.Current = Cursors.WaitCursor;
    
        var client = new HttpClient();
    
        var queryString = HttpUtility.ParseQueryString(string.Empty);
    
        var uri = "https://xxxx.xxx/SendInvoices?" + queryString; 
    
        HttpResponseMessage response;
    
        // Request body
        byte[] byteData = Encoding.UTF8.GetBytes(body);
    
        using (var content = new ByteArrayContent(byteData))
        {
            content.Headers.ContentType = new MediaTypeHeaderValue("application/xml");
            response = await client.PostAsync(uri, content).ConfigureAwait(false);
            string responsebody = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
    
            ParseResponse(response.ToString());
            ParseResponse(responsebody);
    
       }
    }