如何使用异步方法加载Db数据并保持UI响应

时间:2016-06-24 13:52:28

标签: .net vb.net multithreading user-interface asynchronous

我做了一个有点大的应用程序运行良好,除了它的UI(winforms)冻结时使用webclient从web检索数据(链接是嗯...不是最快),或数据连接从数据库中检索查询(存储在远,慢的服务器中 - 无法避免)。

所以我想利用异步方法,以便用户可以移动,最小化并单击窗口而不会让操作系统感到紧张并将其标记为“没有响应”。

我不希望我的代码在这些长度操作之间不做任何其他操作,只是保持UI响应(我知道我应该禁用控件以防止用户在第一次操作完成之前询问其他内容或同样的事情)

但我对异步方法没有任何经验,我的第一次尝试是这样的:

Public Function GetDatatableUIblocked() As DataTable
    Dim retTable As New DataTable
    Dim connection As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\lanserver\storage\DB.accdb;Persist Security Info=False;")
    Dim command = connection.CreateCommand()
    command.CommandText = "SELECT * FROM bdPROC;"
    connection.Open()
    Dim reader = command.ExecuteReader
    retTable.Load(reader)
    connection.Close()
    Return retTable
End Function

Public Function GetDatatableUIfree() As DataTable
    Dim retTable As New DataTable
    Dim connection As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\lanserver\storage\DB.accdb;Persist Security Info=False;")
    Dim command = connection.CreateCommand()
    command.CommandText = "SELECT * FROM bdPROC;"
    connection.Open()
    Dim readerTask = command.ExecuteReaderAsync()

    readerTask.Start() '<=========================== EXCEPTION HAPPENS HERE

    Do
        Application.DoEvents()
    Loop Until readerTask.IsCompleted OrElse readerTask.IsFaulted
    Dim reader = readerTask.Result
    retTable.Load(reader)
    connection.Close()
    Return retTable
End Function

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
    ProgressBar1.Visible = True
    Dim dt = GetDatatableUIblocked()
    ProgressBar1.Visible = False
    DataGridView1.DataSource = dt
End Sub

Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
    ProgressBar1.Visible = True
    Dim dt = GetDatatableUIfree()
    ProgressBar1.Visible = False
    DataGridView1.DataSource = dt
End Sub

然而,我在运行readerTask.Start()时遇到异常,它说“在已完成的任务中无法调用启动”(我正在翻译,我的VS不是英文)。

我在这里检查了一些线索,但说实话,我无法掌握这个概念并将其应用到我的问题中,所以我谦卑地寻求帮助。非常感谢你!

2 个答案:

答案 0 :(得分:1)

让UI免费的一种方法是使用Task。建议的解决方案类似于阻止BackGroundWorker带有无操作循环的点,等待它完成。下面的代码进行了一些其他值得注意的更改:

' note the Async modifier
Private Async Sub btnDoIt_Click(...
    Dim sql = "SELECT * FROM RandomData"
    dtSample = Await Task(Of DataTable).Run(Function() LoadDataTable(sql))
    dgv2.DataSource = dtSample
End Sub

Private Function LoadDataTable(sql As String) As DataTable
    ' NO UI/Control references allowed
    Dim dt = New DataTable
    Using dbcon As New OleDbConnection(ACEConnStr)
        Using cmd As New OleDbCommand(sql, dbcon)
            dbcon.Open()
            dt.Load(cmd.ExecuteReader())
        End Using
    End Using
    Return dt
End Function

我没有OP中提到的确切条件,但我确实有一个500k行的Access表。这对OleDB征税足够,可能需要6-10秒才能加载,这足以说明用户界面是否仍然保持响应。确实如此。

  1. 不要忘记Asynch使用等待Task完成的任何方法。
  2. LoadTable方法设置为从有效查询加载任何表,因此其他东西可以使用它。如果/当查询发生变化时,只需更改调用它的内容。
  3. 当您完成释放资源并防止泄漏时,应该处理诸如Connections和DBCommand对象之类的数据库提供程序对象。 Using块为我们执行此操作。
  4. 不需要执行任何操作/ DoEvents循环,也不需要完成通知的事件。 Await代码将等待加载方法完成,以便之后可以执行的任何其他操作。
  5. Task通常比BackGroundWorker更容易使用。

    <强>资源

答案 1 :(得分:0)

没关系。我用BackgroundWorker获得了我想要的东西:

Public Function GetDatatableUIfree() As DataTable
    Dim connection As New OleDb.OleDbConnection("Provider=Microsoft.ACE.OLEDB.12.0;Data Source=\\lanserver\storage\DB.accdb;Persist Security Info=False;")
    Dim command = connection.CreateCommand()
    command.CommandText = "SELECT * FROM bdPROC;"
    connection.Open()
    Dim retTable As New DataTable, bgw As New BackgroundWorker, bgw_complete As Boolean
    AddHandler bgw.DoWork,
        Sub(sender As Object, e As DoWorkEventArgs) retTable.Load(command.ExecuteReader)
    AddHandler bgw.RunWorkerCompleted,
        Sub(sender As Object, e As RunWorkerCompletedEventArgs) bgw_complete = True
    bgw.RunWorkerAsync()
    Do
        Application.DoEvents()
    Loop Until bgw_complete
    connection.Close()
    Return retTable
End Function

非常感谢你的帮助。