如何在不冻结UI的情况下删除大量行?

时间:2022-02-09 01:28:35

I'm trying to figure out how delete a huge number of records from a DataGridView, by delete I mean run a MySql delete query and remove the row itself from the DataGridView. The issue is do all that without freeze the UI. I'm trying make it work with a BackgroundWorker. I know it still need do a Invoke() once a while and it still freeze the UI but those calls happen relatively fast and all the other other stuff keep going on background later, like the database deletion which is the most consuming-task. If there's a better to do that is very welcome.

我试图弄清楚如何从DataGridView中删除大量记录,通过删除我的意思是运行MySql删除查询并从DataGridView中删除行本身。问题是在不冻结UI的情况下完成所有这些工作。我正在尝试使用BackgroundWorker。我知道它仍然需要暂时执行一次Invoke()并且它仍然会冻结UI但这些调用发生得相对较快,而其他所有其他东西都会继续在后台运行,就像数据库删除这是最耗费任务一样。如果有更好的事情,那是非常受欢迎的。

My current implementation which obviously doesn't work, row.Index is always -1, is the following:

我当前的实现显然不起作用,row.Index始终为-1,如下所示:

on click of button:

点击按钮:

var rows = dataGridView1.SelectedRows;
backgroundWorker.RunWorkerAsync(rows);

then the backgroundWork's doWork event:

然后是backgroundWork的doWork事件:

private void BackgroundWorker_DoWork(object sender, DoWorkEventArgs e)
        {
            using (MySqlConnection con = Database.GetConnection())
            {
                using (MySqlCommand cmd = con.CreateCommand())
                {
                    cmd.Connection = con;
                    con.Open();

                    var SelectedRows =  e.Argument as DataGridViewSelectedRowCollection;
                    int rowIndex = 0;
                    int total = SelectedRows.Count;
                    foreach (DataGridViewRow row in SelectedRows)
                    {
                        int id = getIdByRow_safe(row.Index); //row.Index is -1
        cmd.CommandText = string.Format("delete from xxx where id = {1}", id);

                        bool deleted = cmd.ExecuteNonQuery() == 1;

                        if (!deleted)
                        {
                            /* error handling ... */
                        }

                        int progress = (int)(100.0 / total * ++rowIndex);
                        BackgroundWorker.ReportProgress(progress, row);
                    }
                }
            }
        }

and getId() function and family:

和getId()函数和系列:

  delegate int getIDEventHandler(int i);

    int getIdByRow(int index)
    {
        return (int)dataGridView1.Rows[index].Cells["id"].Value;
    }

    int getIdByRow_safe(int index)
    {
        if (dataGridView1.InvokeRequired)
            return (int) dataGridView1.Invoke(new getIDEventHandler(getIdByRow), new object[] { index });

        return getIdByRow(index);
    }

How do I fix this?

我该如何解决?

1 个解决方案

#1


1  

I recommend transitioning to async/await instead of a Background/Worker. Here's the MSDN about async/await. And here's the example Microsoft has for it's usage:

我建议转换到async / await而不是Background / Worker。这是关于async / await的MSDN。以下是微软为其使用的示例:

// Three things to note in the signature:
//  - The method has an async modifier. 
//  - The return type is Task or Task<T>. (See "Return Types" section.)
//    Here, it is Task<int> because the return statement returns an integer.
//  - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoIndependentWork();

    // The await operator suspends AccessTheWebAsync.
    //  - AccessTheWebAsync can't continue until getStringTask is complete.
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync.
    //  - Control resumes here when getStringTask is complete. 
    //  - The await operator then retrieves the string result from getStringTask.
    string urlContents = await getStringTask;

    // The return statement specifies an integer result.
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
    return urlContents.Length;
}

#1


1  

I recommend transitioning to async/await instead of a Background/Worker. Here's the MSDN about async/await. And here's the example Microsoft has for it's usage:

我建议转换到async / await而不是Background / Worker。这是关于async / await的MSDN。以下是微软为其使用的示例:

// Three things to note in the signature:
//  - The method has an async modifier. 
//  - The return type is Task or Task<T>. (See "Return Types" section.)
//    Here, it is Task<int> because the return statement returns an integer.
//  - The method name ends in "Async."
async Task<int> AccessTheWebAsync()
{ 
    // You need to add a reference to System.Net.Http to declare client.
    HttpClient client = new HttpClient();

    // GetStringAsync returns a Task<string>. That means that when you await the
    // task you'll get a string (urlContents).
    Task<string> getStringTask = client.GetStringAsync("http://msdn.microsoft.com");

    // You can do work here that doesn't rely on the string from GetStringAsync.
    DoIndependentWork();

    // The await operator suspends AccessTheWebAsync.
    //  - AccessTheWebAsync can't continue until getStringTask is complete.
    //  - Meanwhile, control returns to the caller of AccessTheWebAsync.
    //  - Control resumes here when getStringTask is complete. 
    //  - The await operator then retrieves the string result from getStringTask.
    string urlContents = await getStringTask;

    // The return statement specifies an integer result.
    // Any methods that are awaiting AccessTheWebAsync retrieve the length value.
    return urlContents.Length;
}