Lorenzo
Lorenzo

Reputation: 29427

BackgroundWorker that uses HttpClient async methods

I have used in the past the BackgroundWorker in a Windows Form application. For my new exercise I need to use async methods inside the worker and I am getting a little bit confused about that.

This is my code structure. In the form load event I am creating the BackgroundWorker object and setup events

private void fMain_Load( object sender, EventArgs e ) {
    bw = new BackgroundWorker();
    bw.WorkerReportsProgress = true;
    bw.DoWork += new DoWorkEventHandler( bw_DoWork );
    bw.ProgressChanged += new ProgressChangedEventHandler( bw_ProgressChanged );
    bw.RunWorkerCompleted += new RunWorkerCompletedEventHandler( bw_RunWorkerCompleted );
}

When the user click a button I am starting the worker

private void btnGenerate_Click( object sender, EventArgs e ) {
    Settings settings = new Settings();
    pbCounter.Visible = true;
    btnGenerate.Enabled = false;
    bw.RunWorkerAsync( settings );
}

And this is the worker code

private async void bw_DoWork( object sender, DoWorkEventArgs e ) {
    try {
        for ( int ix = 1; i <= 100; i++ ) {
            using (var client = new HttpClient()) {
                [???? how to call and wait here ????]
                HttpResponseMessage response = await client.PostAsync( "endpoint", new StringContent( JsonConvert.SerializeObject( formContent ), Encoding.UTF8, "application/json" ) );
            }

            //The counter will keep track of your process
            Application.DoEvents();
            int percentage = ix * 100 / settings.TotalRuns;
            bw.ReportProgress( percentage );
        }
    }
    catch ( Exception ex ) {
        MessageBox.Show( ex.Message, "Gift Creator", MessageBoxButtons.OK, MessageBoxIcon.Error );
    }
}

Upvotes: 0

Views: 1777

Answers (1)

Ren&#233; Vogt
Ren&#233; Vogt

Reputation: 43886

If you use async and await there is no need for a background worker. In fact, your background worker would not work because the control is given back to the caller when you use await. And when the DoWork handler returns control to its caller, the background worker will terminate and not continue its remainnig tasks.

So I would make the button handler async and do the http requests there:

    private async void btnGenerate_Click(object sender, EventArgs e)
    {
        const int totalRuns = 5;
        pbCounter.Visible = true;
        pbCounter.Minimum = 0;
        pbCounter.Maximum = totalRuns;
        pbCounter.Value = 0;
        btnGenerate.Enabled = false;

        try
        {
            for ( int i = 1; i <= totalRuns; i++ ) 
            {
                using (var client = new HttpClient()) 
                    await client.PostAsync( "endpoint", new StringContent( JsonConvert.SerializeObject( formContent ), Encoding.UTF8, "application/json" ) );
                pbCounter.Value = i;

            }
        }
        catch (Exception ex )
        {
            MessageBox.Show( ex.Message, "Gift Creator", MessageBoxButtons.OK, MessageBoxIcon.Error );
        }

        btnGenerate.Enabled = true;
    }

So this handler gives back the control to the caller (to say it over-simplified: the UI) while waiting for the http request. When the request is finished, the execution is continued at the pbCounter.Value = i line - and on the UI thread! So you can safely update the progress bar, because you're not doing it from another thread.

I hope this helps you. Note that I set the pbCounter.Maximum to the number of loops you're about to run, so you don't need to calculate the percentage.

Upvotes: 2

Related Questions