Load data asynchronously into DataTable in Windows Forms

I'm modifying the data access layer of an old WinForms/ADO app to get Salesforce objects using soql over the asynchronous developerforce rest api. The following approach generally works, except that one of my thirteen tables routines never returns from the AddMyTableRow (System.Data.DataRowCollection.Add()) method:

public void LoadData() {
    var tasks = new List<Task<int>> {
        GetObject1Async(),
        GetObject2Async(),...
        GetObject13Async()
    };
    Task.WaitAll(tasks.ToArray());
    //do some postprocessing now that I have all the data
}

async Task<int> GetObject1Async(){
    var response = await cnx.QueryAsync<dynamic>("SELECT foo FROM bar").ConfigureAwait(false);
    foreach (var rec in response.Records){
        var row = MyDataSet.MyTable.NewMyTableRow();
        row.Name = rec.Name; ...
        MyDataSet.MyTable.AddMyTableRow(row); //<== Sometimes this never returns
    }
    return MyDataSet.MyTable.Count;
}

Is this even the right approach?

I've found that if I comment out the whole await cnx.Query... line and just call AddMyTableRow(dummyData) it works. I feel like there is some kind of async /threading / context something going on, but since its not generating any kind of error or exception I don't know what to go on.

Cheers, Jeff


Solution 1:

You can have an async method which returns Task<DataTable> and then in async form Load event handler or in the async method which you want to perform data-binding, await call it, then use the result to bind to the grid.

Example

public async Task<DataTable> GetDataAsync(string command, string connection)
{
    var dt = new DataTable();
    using (var da = new SqlDataAdapter(command, connection))
        await Task.Run(() => da.Fill(dt));
    return dt;
}

private async void Form1_Load(object sender, EventArgs e)
{
    var command = @"SELECT * FROM Category";
    var connection = @"Your Connection String";
    var data = await GetDataAsync(command, connection);
    this.dataGridView1.DataSource = data;
}

To see how you can show a loading animation over the DataGridView, take a look at this post.