How to use async methods to load Db Data and keep UI responsive

前端 未结 2 358
梦毁少年i
梦毁少年i 2021-01-15 15:01

I made a somewhat large application that works well, except for its UI (winforms) freezes when using webclient to retrieve data from web (link is um... not the fastest), or

相关标签:
2条回答
  • 2021-01-15 15:30

    One way to leave the UI free is to use a Task. The proposed solution sort of thwarts the point of a BackGroundWorker with a do nothing loop to wait for it to complete. The code below makes a few other noteworthy changes:

    ' 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
    

    I dont have the exact conditions mentioned in the OP, but I do have an Access table with 500k rows. This taxes OleDB enough that it can take 6-10 seconds to load which is plenty long enough to tell if the UI remains responsive. It does.

    1. Don't forget to use Asynch on whatever method will be awaiting the Task to complete.
    2. The LoadTable method is set up to load any table from a valid query, so other things can use it. If/when queries change, just make changes to the things calling it.
    3. DB Provider objects such as Connections and DBCommand objects ought to be disposed when you are done with the to release resources and prevent leaks. The Using blocks do this for us.
    4. There is no need for a do-nothing/DoEvents loop, nor an event for a completed notice. The Await code will wait for the load method to complete so that any other things you need to do can be done afterwards.

    A Task can often be simpler to use than a BackGroundWorker.

    Resources

    • Task Class
    • Asynchronous Programming with Async and Await (Visual Basic)
    • Using Statement
    0 讨论(0)
  • 2021-01-15 15:44

    never mind. I've got what I wanted with a BackgroundWorker instead:

    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
    

    Thank you very much anyway for the help.

    0 讨论(0)
提交回复
热议问题