【问题标题】:C# - DataReader skipping first resultC# - DataReader 跳过第一个结果
【发布时间】:2019-11-27 19:20:08
【问题描述】:

想看看我能不能得到一些帮助。我有一个BackgroundWorker,它将执行SQL 查询,然后在进度更改事件期间更新DataGridView。但是,它会跳过第一行并将重复的最后一行添加到DGV。当我在 UID 中添加以进行故障排除时,列标题已关闭。所以请忽略标题与数据不匹配的地方。

我检查了Read()IF 中的常见罪魁祸首,但事实并非如此。

有趣的是,如果我在reader = Sqlcmd.ExecuteReader(); 上休息一下并单步执行它,它会随机工作。

任何帮助将不胜感激!我完全不知所措。


此外,如果我可以将 DGV 添加从进度更改为完整事件,那也将是一个巨大的好处。

/// DO WORK
private void issueBWworker_DoWork_1(object sender, DoWorkEventArgs e)
{
    // DGV 1
    RetriveTableData Obj = (RetriveTableData)e.Argument;
    string SqlcmdString = "SELECT * FROM xBETA_OAP_ISSUE";
    SqlDataReader reader;
    int i = 1;
    try
    {
        using (SqlConnection conn = new SqlConnection(ConnString))
        {
            Sqlcmd = new SqlCommand(SqlcmdString, conn);
            conn.Open();
            reader = Sqlcmd.ExecuteReader();
            if (reader.HasRows)
            {
                while (reader.Read())
                {
                    Obj.uid = reader["id"].ToString();
                    Obj.iss_type = reader["issue_type"].ToString();
                    Obj.inc_num = reader["ticket_num"].ToString();
                    Obj.create_date = reader["create_date"].ToString();
                    Obj.created_by = reader["created_by"].ToString();
                    Obj.active = reader["active"].ToString();
                    Obj.change_date = reader["change_date"].ToString();
                    Obj.changed_by = reader["changed_by"].ToString();

                    Thread.Sleep(100);

                    //MessageBox.Show(Obj.uid);

                    // To Report progress.x
                    issueBWworker.ReportProgress(i, Obj);
                    if (issueBWworker.CancellationPending)
                    {
                        // Set the e.Cancel flag so that the WorkerCompleted event
                        // knows that the process was cancelled.
                        e.Cancel = true;
                        issueBWworker.ReportProgress(0);
                        return;
                    }
                    i++;
                }
                conn.Close();
            }
        }
    }
    catch (Exception ex)
    {
        MessageBox.Show(ex.Message);
    }
}
/// Progress Changes

private void issueBWworker_ProgressChanged_1(object sender, ProgressChangedEventArgs e)
{
    if (!issueBWworker.CancellationPending)
    {
        // DGV 1
        RetriveTableData Obj = (RetriveTableData)e.UserState;
        issue_dgv.Rows.Add(Obj.uid.ToString(), Obj.iss_type.ToString(), Obj.inc_num.ToString(), Obj.create_date.ToString(), Obj.created_by.ToString(), Obj.active.ToString(), Obj.change_date.ToString(), Obj.changed_by.ToString());

        pbar.Value = e.ProgressPercentage;
        //toolStripStatusLabel1.Text = "Processing row.. " + e.ProgressPercentage.ToString() + " of " + TotalRecords;
    }
}
/// Complete
private void issueBWworker_RunWorkerCompleted_1(object sender, RunWorkerCompletedEventArgs e)
{
    if (e.Cancelled)
    {
        //toolStripStatusLabel1.Text = "Cancelled by User Intentionally...";
        pbar.Value = 0;
        pbar.Visible = false;
    }
    // Check to see if an error occurred in the background process.
    else if (e.Error != null)
    {
        //toolStripStatusLabel1.Text = e.Error.Message;
        pbar.Visible = false;
    }
    else
    {
        // BackGround Task Completed with out Error
        pbar.Visible = false;
        //toolStripStatusLabel1.Text = " All Records Loaded...";
    }
}

【问题讨论】:

  • 添加到将解决您的主要问题的答案。 1)您也可以删除if (reader.HasRows)并直接从while (reader.Read())开始,因为如果阅读器没有行,您什么都不做。 2)您不需要conn.Close(); using 将负责关闭和处理连接。 3) SqlCommandSqlReader 都是一次性对象,也就是说,您需要像使用 SqlConnection 一样在 using 块中创建它们。祝你好运。

标签: c# datagridview backgroundworker sqldatareader


【解决方案1】:

根据documentation,在备注部分中提到对ReportProgress 的调用是异步的并立即返回。但是,为了使其按预期与您的代码一起工作,您需要它同步运行。

这是因为您使用相同的对象 Obj 将阅读器每次迭代的数据传递给 ReportProgress - 并且可能使用 Thread.Sleep(100) 作为同步手段 - 这并不是真正的有效或有效的设计,并可能导致您看到的行为。

例如,假设数据有四行,结果如下:

+--------+-----------------------------------+--------------------------------------------+
|        | DoWork                            | ReportProgress                             |
+--------+-----------------------------------+--------------------------------------------+
| 1      | Populate values of Obj with row 1 |                                            |
+--------+-----------------------------------+--------------------------------------------+
| 2      | Wait 100 ms                       |                                            |
+--------+-----------------------------------+--------------------------------------------+
| 3      | Dispatch ReportProgress           |                                            |
+--------+-----------------------------------+--------------------------------------------+
| 4      | Populate values of Obj with row 2 |                                            |
+--------+-----------------------------------+--------------------------------------------+
| 5      | Wait 100 ms                       | Add a row with values of Obj, now at Row 2 |
+--------+-----------------------------------+--------------------------------------------+
| 6      | Dispatch ReportProgress           |                                            |
+--------+-----------------------------------+--------------------------------------------+
| 7      | Populate values of Obj with row 3 |                                            |
+--------+-----------------------------------+--------------------------------------------+
| 8      | Wait 100 ms                       | Add a row with values of Obj, now at Row 3 |
+--------+-----------------------------------+--------------------------------------------+
| 9      | Dispatch ReportProgress           |                                            |
+--------+-----------------------------------+--------------------------------------------+
| 10     | Populate values of Obj with row 4 |                                            |
+--------+-----------------------------------+--------------------------------------------+
| 11     | Wait 100 ms                       | Add a row with values of Obj, now at Row 4 |
+--------+-----------------------------------+--------------------------------------------+
| 12     | Dispatch ReportProgress           |                                            |
+--------+-----------------------------------+--------------------------------------------+
| 13     | End of iteration, no more rows    | Add a row with values of Obj, now at Row 4 |
+--------+-----------------------------------+--------------------------------------------+

如何解决?

最简单的方法是在每次迭代中使用Obj 的新实例。这样,每次调用ReportProgress 都有自己的实例,不会被更改。

换句话说,像下面这样假设RetrieveTableData 是一个简单的对象,它的构造函数不需要参数。我故意保持你原来的风格,而不是重写所有东西。我们可以在这里讨论很多问题,代码风格,变量名称,但这与问题没有直接关系......

while (reader.Read())
{
    var Obj = new RetriveTableData(); // create a new instance
    Obj.uid = reader["id"].ToString();
    Obj.iss_type = reader["issue_type"].ToString();
    ...

然后我们将不再需要在方法顶部的这一行;关键是要更改Obj 的范围,这样我们就不会在每次迭代中重用它。

RetriveTableData Obj = (RetriveTableData)e.Argument;

Thread.Sleep(100) 也将不再需要。

为了解决在完成事件而不是进度事件中创建数据行的额外目标,您可以将这些单独的 RetrieveTableData 对象收集到 List<RetrieveTableData> 中,然后通过 @ 将其传递给您的 RunWorkerCompleted 事件处理程序987654337@DoWorkEventArgs

【讨论】:

  • 谢谢史蒂夫。我现在正在努力做这件事,但我对 C# 很陌生。关于从哪里开始创建新实例有什么建议吗?
  • @MattOliver 我已编辑以提供有关如何解决此问题的想法
  • 绝对完美!它也显着加快了速度,但这是可以预料的,可以消除睡眠。非常感谢您清晰简洁的帮助!
猜你喜欢
  • 2011-01-07
  • 1970-01-01
  • 2018-06-01
  • 1970-01-01
  • 1970-01-01
  • 2013-03-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多