【问题标题】:How can I best implement async into my WPF application?我怎样才能最好地在我的 WPF 应用程序中实现异步?
【发布时间】:2020-10-24 04:50:34
【问题描述】:

我正在开发一个运行 SQL 查询并在按下时返回结果的数据网格的 WPF 应用程序。 目前,我尝试找出异步方法的结构如下:

public static async Task GenerateImportDataGrid( TextBox TBImportData )
{
    using( SqlConnection conn = new SqlConnection( sqlConnStr ) )
    {
        SqlTransaction transaction = conn.BeginTransaction( "InsertFromFile" );
        using( var package = new ExcelPackage( new FileInfo( TBImportData.Text ) ) )
            {
                ExcelWorksheet worksheet = package.Workbook.Worksheets.FirstOrDefault();
                int rows = worksheet.Dimension.Rows - 1;
                int columns = worksheet.Dimension.Columns - 1;
                int rowOffset = 4; //offset due to headers

                for( int i = 1; i <= rows - rowOffset; i++ ) //loop through each row
                {
                    SqlCommand insert = new SqlCommand(
                        string.Format(
                            "INSERT INTO ##COA ( mailPreparerName, barcodeEdoc, errorType, errorCode, errorDescription, errorData, jobID, mailClassEdoc, processingCategoryEdoc, mailOwnerName, scanTime) VALUES ('{0}', '{1}', '{2}', '{3}', '{4}', '{5}', '{6}', '{7}', '{8}', '{9}', '{10}')",
                            worksheet.Cells[ i + rowOffset, 2 ].Value, worksheet.Cells[ i + rowOffset, 4 ].Value,
                                worksheet.Cells[ i + rowOffset, 8 ].Value, worksheet.Cells[ i + rowOffset, 9 ].Value,
                                worksheet.Cells[ i + rowOffset, 10 ].Value, worksheet.Cells[ i + rowOffset, 11 ].Value,
                                worksheet.Cells[ i + rowOffset, 13 ].Value, worksheet.Cells[ i + rowOffset, 18 ].Value,
                                worksheet.Cells[ i + rowOffset, 19 ].Value, worksheet.Cells[ i + rowOffset, 34 ].Value,
                                worksheet.Cells[ i + rowOffset, 39 ].Value ),
                            conn, transaction );
                    insert.ExecuteNonQuery();
                }
                transaction.Commit();
            }
                    
            SqlCommand cmd = new SqlCommand( "EXEC Export", conn );
            SqlDataReader reader = await cmd.ExecuteReaderAsync();
    
            DataTable resultSet = new DataTable();
            await Task.Run( () => resultSet.Load( reader ) );
    
            DataDisplay results = new DataDisplay( resultSet ); //DataDisplay is just a window containing a DataGrid
            results.Show();
    }
}
private async void Run()
{
    ...
    PB_ProcessProgress.IsIndeterminate = true;
    await Reporting.GenerateImportDataGrid( TBImportData );
}
private void BtnView_Click( object sender, RoutedEventArgs e )
{
      ...
      Run();
}

根据运行的查询,我的应用程序可以获取 30 秒到 20 分钟之间的任何时间。我的应用程序上有一个进度条,我真的很想在处理数字时使用它,但是我很难弄清楚如何最好地实现asyncawait,无论是在我的函数中还是在它被调用的时候。

非常感谢任何帮助!

【问题讨论】:

  • cmd.ExecuteReader() 替换为await cmd.ExecuteReaderAsyn()。将您的方法声明为 async Task 而不是 void,并在 async void Click 处理程序方法中调用(并等待)它。
  • 不是你问的,但我建议你看看 dapper。它在 ado 上提供了扩展方法,可以更轻松地从数据表和数据集移动到 dto 列表。
  • @Clemens 感谢您的回复!我尝试实施您的建议以及 Johnathan Barclay 对 await Task.Run(() =&gt; resultSet.Load(reader)); 的建议,但应用程序仍然锁定 :( 你能检查我的新编辑并再次帮助我吗?
  • @sirius_pain 什么是插入物?你能添加那个代码吗?
  • 您仍在使用一些阻塞调用ExecuteNonQueryCommit。此外,您应该参数化您的 SQL 语句,而不是使用易于 SQL 注入的string.Join

标签: c# sql wpf asynchronous


【解决方案1】:

ADO.NET 的 async 等效于 CommitExecuteReaderAsync

如果您将方法设为async,则可以使用并等待这些:

public static async Task GenerateImportDataGrid(TextBox TBImportData)
{
    using (SqlConnection conn = new SqlConnection(sqlConnStr))
    using (SqlTransaction transaction = conn.BeginTransaction("InsertFromFile");
    {
        //bunch of inserts are executed as part of the transaction
        await transaction.CommitAsync();
            
        using (SqlCommand cmd = new SqlCommand("EXEC Export", conn))
        {
            SqlDataReader reader = await cmd.ExecuteReaderAsync();
        }

        DataTable resultSet = new DataTable();
        resultSet.Load(reader);

        DataDisplay results = new DataDisplay(resultSet); //DataDisplay is just a window containing a DataGrid
        results.Show();
    }
}

这样做会在异步工作发生时释放您的 UI 线程,让您的应用程序保持响应。


如果您发现resultSet.Load(reader) 也很昂贵,这也可能会锁定您的应用程序。

由于这是 CPU 密集型工作,您必须通过在线程池上运行来缓解这种情况:

await Task.Run(() => resultSet.Load(reader));

这也将释放 UI 线程。

【讨论】:

    猜你喜欢
    • 2020-10-04
    • 2013-09-28
    • 2015-07-30
    • 1970-01-01
    • 2021-04-26
    • 1970-01-01
    • 2020-03-24
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多