我有一个数据表,其中根据互联网上可用的数据添加行。每次添加一行时,我都会在该行上放置一个时间戳。该方法已经运行多年,数百名用户没有出现任何错误。我现在希望根据数据的年龄从数据表中删除行。我有一个计时器,可以触发每个用户定义的时间间隔,然后搜索所有较旧的行并删除它们。问题是,我收到了各种似乎发生变化的错误,即使我对所有方法都有 try catch 语句,但错误也没有被捕获。该错误仅在program.cs 中的整个应用程序上的错误捕获时捕获 错误是“退出错误:未将对象引用设置为对象的实例。在 System.Windows.Forms.DataGridViewCell.PaintWork, 或退出错误:索引超出范围。必须为非负数且小于集合的大小。 参数名称:System.Collections.ArrayList.get_Item处的索引(Int32索引)
或 退出错误:未将对象引用设置为对象的实例。在 System.Windows.Forms.DataGridViewTextBoxCell.PaintPrivate
但其余错误中没有任何有用的信息。 这是代码:
public void ageTimer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
{
try
{
spotAge = Convert.ToDouble(Properties.Settings.Default.SpotAge);
//Select all rows in datatable where the age is older than spotAge and delete them.
//date is stored in dt as 20:12:2017: 21:49:02 derived from DateTime.UtcNow.ToString("dd:MM:yyyy: HH:mm:ss")
//spotTime is the time from the table
DateTime spotTime;
dt.AcceptChanges();
var rowCount = dt.Rows.Count;
for (int i = rowCount -1; i >= 0; i--)
{
DataRow dr = dt.Rows[i];
spotTime = DateTime.ParseExact(Convert.ToString(dr["date"]), "dd:MM:yyyy: HH:mm:ss", System.Globalization.CultureInfo.InvariantCulture);
if (spotTime.AddMinutes(spotAge) <= DateTime.UtcNow)
{
dr.Delete();
}
}
dt.AcceptChanges();
dataGridView1.Update();
dataGridView1.Refresh();
dataGridView1.FirstDisplayedScrollingRowIndex = 0;
}
catch (Exception x)
{
}
}
现在有趣的是,在我的系统上,这根本不会崩溃。但是,如果我在调试模式下运行 Visual Studio,就会出现这种情况。它也会对少数用户造成崩溃。但不是所有人。
我尝试了相同的代码,除了我总是只删除一行并且它工作得很好。我尝试删除除第一行、前两行之外的每一行,但它仍然崩溃。
根据下面的评论指出计时器在单独的线程上运行,我添加了以下内容:
public void ageTimer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
{
DeleteRows("");
}
上面的代码现在具有以下内容:
private void DeleteRows(string details)
{
if (InvokeRequired)
{
this.Invoke(new Action<string>(DeleteRows), new object[] { details});
return;
}
其余代码如上。这样就彻底解决了这个问题。 非常感谢!
再次阅读您的问题。这已经工作了好几年了。现在它不起作用,因为您添加了计时器,在新的单独线程中运行。 这可能是一个竞争条件。 显然,dt是一个共享资源,在多线程环境中不能正确使用。 假设
dt
是数据网格视图的源,在一种情况下,用户可能会用它做一些事情(只需滚动就足以从 dt 读取以便绘制新的可见行 - 因此,我想您会收到 PaintWork 错误从那里开始,状态同时被计时器事件处理程序更改)。想象一下用户从网格中删除行,这反映到底层源dt
,但是,您的计时器刚刚恰好删除了这些行,并且您得到索引超出范围异常。
您可以尝试制作它的副本吗
dt
,而不是在计时器事件处理程序中使用dt_copy
(只是注意不要制作浅副本)。进行更改,完成后,只需将该副本绑定为 datagridview 的新源,一切都应该很好。
另一种可能执行速度更快的方法是调用存储过程并直接通过数据库进行删除,然后在调用返回后刷新您的
dt
(只需重新填充dt
)。
在这两种情况下,根据删除的速度,您应该检查“dt”是否脏(同时由用户更新),并且可能想要合并这些更改,这样它们就不会丢失。
这里的问题是,您在 for 循环中调用了 delete row.delete 语句。这是操纵行集合的长度,因此您会得到索引越界异常。
来自 msdn 的一句话:
在迭代 DataRowCollection 对象时,不应在 foreach 循环中调用Delete。删除会修改集合的状态。
删除 DataGridView 中的一行后,我需要使用计时器来延迟将数据重新加载到网格视图中。 我永远无法通过任何正常方式捕获该异常错误。尝试/捕获根本没有任何好处!
我使用了这个序列: 触发 dgrid_UserDeletingRow 事件 删除该行的数据库记录 删除该行的数组数据(数据网格未与数据绑定) 启动定时器延迟(1ms) 计时器从数据库重新加载 dataGridView 异常错误消失了!