C# - DataReader 跳过第一个结果
C# - DataReader skipping first result
想看看我是否能在这方面得到一些帮助。我有一个 BackgroundWorker
将执行 SQL 查询,然后在进度更改事件期间更新 DataGridView
。但是,它会跳过第一行并将重复的最后一行添加到 DGV
。 headers 列已关闭,因为我在 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...";
}
}
根据 documentation,在备注部分中提到对 ReportProgress
的调用是异步的,returns 是立即调用。但是,为了使其与您的代码一起按预期工作,您需要它同步 运行。
这是因为您正在使用同一个对象 Obj
,将 reader 每次迭代的数据传递给 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
事件处理程序通过 e.Result
在 DoWorkEventArgs
.
想看看我是否能在这方面得到一些帮助。我有一个 BackgroundWorker
将执行 SQL 查询,然后在进度更改事件期间更新 DataGridView
。但是,它会跳过第一行并将重复的最后一行添加到 DGV
。 headers 列已关闭,因为我在 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...";
}
}
根据 documentation,在备注部分中提到对 ReportProgress
的调用是异步的,returns 是立即调用。但是,为了使其与您的代码一起按预期工作,您需要它同步 运行。
这是因为您正在使用同一个对象 Obj
,将 reader 每次迭代的数据传递给 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
事件处理程序通过 e.Result
在 DoWorkEventArgs
.