Getting a Cannot await void, on a method that I ha

2019-05-04 09:59发布

I'm on a team writing a WPF app. We have to make it so that when a user hides/shows different columns that it will reflect that in a ReportViewer control on one of the views. In testing we've found that it takes a long time to add the data to the ReportViewer's data sources; sometimes on the order of several seconds to possibly a minute. Too long I think for the users. So I'm trying to use C#'s async and await. However, when I applied await to the line that is the process hog and then compile it, I get an error from the C# compiler, "Cannot await 'void'". In this case, I cannot change what the .NET framework returns, its void. So how do I handle this situation? Here's the code:

private async Task GenerateReportAsync()
{
    DataSet ds = new DataSet();
    DataTable dt = new DataTable();
    dt.Clear();
    int iCols = 0;
    //Get the column names
    if (Columns.Count == 0)     //Make sure it isn't populated twice
    {
        foreach (DataGridColumn col in dataGrid.Columns)
        {
            if (col.Visibility == Visibility.Visible)
            {
                Columns.Add(col.Header.ToString());     //Get the column heading
                iCols++;
            }
        }
    }
    //Create a DataTable from the rows
    var itemsSource = dataGrid.ItemsSource as IEnumerable<Grievance>;
    if (this.rptViewer != null)
    {
        rptViewer.Reset();
    }
    rptViewer.LocalReport.DataSources.Clear();
    if (m_rdl != null)
    {
        m_rdl.Dispose();
    }
    Columns = GetFieldOrder();
    m_rdl = CoreUtils.GenerateRdl(Columns, Columns);
    rptViewer.LocalReport.LoadReportDefinition(m_rdl);
    //the next line is what takes a long time
    await rptViewer.LocalReport.DataSources.Add(new ReportDataSource("MyData", CoreUtils.ToDataTable(itemsSource)));
    rptViewer.RefreshReport();
}

2条回答
ゆ 、 Hurt°
2楼-- · 2019-05-04 10:40

I can almost be certain that the entire line is not the slow line, it is much more likely that CoreUtils.ToDataTable(itemsSource) is the slow piece and that is the part that needs to be improved.

You did not include the source of ToDataTable so I can't say for sure what the best way would be, the first and best option would be write a new version of the function that leverages async calls internally to create a function that allows you to await it.

var data = await CoreUtils.ToDataTableAsync(itemsSource)
rptViewer.LocalReport.DataSources.Add(new ReportDataSource("MyData", data));

The second, less performant option is do the slow part on a background thread using Task.Run then back on the UI thread set the data source as needed.

var data = await Task.Run(() => CoreUtils.ToDataTable(itemsSource));
rptViewer.LocalReport.DataSources.Add(new ReportDataSource("MyData", data));
查看更多
Bombasti
3楼-- · 2019-05-04 10:42

For methods that are inherently synchronous, you need to wrap them in your own Task so you can await it. In your case, I would just use Task.Run:

await Task.Run(() => 
{
   rptViewer.LocalReport.DataSources.Add(new ReportDataSource("MyData", CoreUtils.ToDataTable(itemsSource)));
});

There are other ways to generate a task, but this one is probably the easiest. If this updates a UI component, it will likely throw an exception because those operations must occur on the UI thread. In this case, try to get the UI related piece away from the long-running part and only wrap the long part in the Task.

查看更多
登录 后发表回答