我有一个数据表,其中根据互联网上可用的数据添加行。每次添加一行时,我都会在行上添加一个时间戳。这已经工作了很多年,有数百名用户没有发生错误。我现在希望根据数据的年龄从数据表中删除行。我有一个计时器,它触发每个用户定义的间隔,然后我搜索所有较旧的行,然后删除它们。问题是,我收到的各种错误似乎都有所改变,即使我在所有方法上都尝试了catch语句,也不会发现错误。仅在program.cs中整个应用程序的错误捕获时捕获该错误 错误是“退出错误:对象引用未设置为对象的实例。在System.Windows.Forms.DataGridViewCell.PaintWork, 或退出错误:索引超出范围。必须是非负数且小于集合的大小。 参数名称:System.Collections.ArrayList.get_Item(Int32 index)
中的索引或 退出错误:对象引用未设置为对象的实例。在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;
}
,其余代码如上。这完全解决了这个问题。 非常感谢!
答案 0 :(得分:1)
再次阅读你的问题。这工作多年了。现在它不起作用,因为你添加了计时器,在一个新的独立线程中运行。
这可能是竞争条件。
显然,dt是一种在多线程环境中未正确使用的共享资源。
假设dt
是数据网格视图的来源,在一种情况下,用户可能正在做一些事情(只需滚动就足以从dt读取以绘制新的可见行 - 因此,我想你得到了PaintWork错误,因为状态由计时器事件处理程序同时更改。想象一下,用户从网格中删除行,这反映到底层源dt
,但是,你的计时器恰好刚刚删除了那些行,你得到索引超出范围异常。
不是在计时器事件处理程序中使用dt
,而是可以尝试复制它dt_copy
(只需注意不要制作浅层副本)。进行更改,一旦完成,只需将该副本绑定为datagridview的新来源,一切都应该是好的。
另一种可能执行速度更快的方法是调用存储过程并直接通过数据库删除,只需在调用返回后刷新dt
(仅重新填充dt
)。
在这两种情况下,根据删除的速度,你应该检查'dt'是否脏(由用户同时更新),并且可能想要合并这些更改,以免它们丢失。
答案 1 :(得分:0)
这里的问题是,你在for循环中调用delete row.delete语句。这会操纵行集合的长度,从而使索引超出绑定异常。
来自msdn的一句话:
在迭代DataRowCollection对象时,不应在foreach循环中调用Delete。删除修改集合的状态。