简体   繁体   English

删除数据表中的行时,DataGridView中无法捕获的异常

[英]Uncatchable exception in DataGridView when deleting rows in datatable

I have a data table in which rows are added based on data available on the internet. 我有一个数据表,其中根据互联网上可用的数据添加行。 Each time I add a row, I also put a timestamp on the row. 每次添加一行时,我都会在该行上添加一个时间戳。 This has been working for a number of years, with hundreds of users with no errors occurring. 这已经工作了很多年,数百名用户都没有发生任何错误。 I now wish to delete rows from the data table based on the age of the data. 现在,我希望根据数据的使用期限从数据表中删除行。 I have a timer that fires every user defined interval and then I search for all rows that are older and I delete them. 我有一个计时器,它会触发每个用户定义的时间间隔,然后搜索所有较旧的行并将其删除。 The problem is, that I am receiving various errors that seem to change and even though I have try catch statements on all methods, the errors are not caught. 问题是,我收到的各种错误似乎都在改变,即使我在所有方法上都尝试了catch语句,也未捕获到错误。 The error is caught only on an error catch on the whole application in program.cs The errors are "Exit error: Object reference not set to an instance of an object. at System.Windows.Forms.DataGridViewCell.PaintWork, or Exit error: Index was out of range. Must be non-negative and less than the size of the collection. Parameter name: index at System.Collections.ArrayList.get_Item(Int32 index) 仅在program.cs中整个应用程序的错误捕获中捕获该错误,这些错误是“退出错误:未将对象引用设置为对象的实例。在System.Windows.Forms.DataGridViewCell.PaintWork,或退出错误:索引超出范围,必须为非负数且小于集合的大小参数名称:System.Collections.ArrayList.get_Item(Int32 index)处的索引

or Exit error: Object reference not set to an instance of an object. 或退出错误:对象引用未设置为对象的实例。 at System.Windows.Forms.DataGridViewTextBoxCell.PaintPrivate 在System.Windows.Forms.DataGridViewTextBoxCell.PaintPrivate

but there is no useful information in the rest of the errors. 但是其余的错误中没有有用的信息。 Here is the code: 这是代码:

    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)
        {

        }
    }

Now the interesting thing is that on my system this will not crash at all ever. 现在有趣的是,在我的系统上这根本不会崩溃。 However, if I run visual studio in debug mode it will. 但是,如果我在调试模式下运行Visual Studio,它将可以。 It will also crash on a small number of users as well. 它还会在少数用户上崩溃。 But not everyone. 但不是所有人。

I tried the same code except I always will delete only one row and it works perfectly fine. 我尝试了相同的代码,除了我总是会只删除一行并且它工作得很好。 I tried deleting every row except for the first, the first two and it still crashes. 我尝试删除除第一行,前两行之外的所有行,但仍然崩溃。

Based on the comment below which pointed out that the timer was running on a separate thread, I added the following: 根据下面的评论指出计时器在单独的线程上运行,我添加了以下内容:

    public void ageTimer_Elapsed(object source, System.Timers.ElapsedEventArgs e)
    {
        DeleteRows("");
    }

and the above code now now has the following: 上面的代码现在具有以下内容:

private void DeleteRows(string details)
{
  if (InvokeRequired)
  {
     this.Invoke(new Action<string>(DeleteRows), new object[] { details});
     return;
  }

and the rest of the code is as above. 其余代码如上。 This solves the issue completely. 这样可以彻底解决问题。 Many thanks! 非常感谢!

Just read your question again. 请再次阅读您的问题。 This was working for years. 这已经工作了好几年了。 Now it's not working, cause you added timer, running in a new, separate thread. 现在它不起作用,因为您添加了计时器,并在新的单独线程中运行。 This might be a race condition. 这可能是比赛条件。 Obviously, dt is a shared resource which is not properly used in a multithreaded environment. 显然,dt是一种共享资源,在多线程环境中不能正确使用。 Assuming dt is source of your data grid view, in one situation, user might be doing something with it (just scrolling would be enough to read from dt in order to paint newly visible rows - thus, I suppose you get the PaintWork error from there, as the state was changed in the mean time by a timer event handler). 假设dt是数据网格视图的源,在一种情况下,用户可能正在使用它(只需滚动就足以从dt中读取内容,以便绘制新的可见行-因此,我想您会从那里得到PaintWork错误,因为状态在平均时间内被计时器事件处理程序更改了)。 Imagine user deletes row from the grid, that reflects to the underlying source dt , but, your timer just happened to delete those rows just a moment ago and you get index out of range exception. 想象一下,用户从网格中删除了行,这反映了基础源dt ,但是,您的计时器刚好在刚才删除了这些行,并且您获得了索引超出范围的异常。

Instead of working with dt in your timer event handler, can you try and make a copy of it dt_copy (just pay attention not to make a shallow copy). 除了可以在计时器事件处理程序中使用dt ,还可以尝试制作它的副本dt_copy (只是注意不要制作浅表副本)。 Make your changes, and once you're done, just bind that copy as a new source of datagridview and all should be good. 进行更改,完成后,只需将该副本绑定为新的datagridview源即可,一切都会很好。

Another approach, which also might perform faster would be to call stored procedure and do deletion by database directly and just refresh your dt once that call returns (just repopulate dt ). 另一种可能执行起来也更快的方法是调用存储过程并直接由数据库删除,并在调用返回后立即刷新dt (只需重新填充dt )。

In both cases, depending how fast deletion is, you should check if 'dt' is dirty (updated by a user in the mean time) and might want to merge those changes so they are not lost. 在这两种情况下,取决于删除的速度,您应该检查'dt'是否脏(同时由用户更新),并且可能希望合并这些更改,以免丢失。

The problem over here is, that you´r calling the delete row.delete statement inside your for loop. 这里的问题是,您要在for循环中调用delete row.delete语句。 This is manipulating the length of your row collection thus you get the index out of bound exception. 这是在操纵行集合的长度,从而使索引超出绑定异常范围。

One quote from msdn: 来自msdn的一句话:

Delete should not be called in a foreach loop while iterating through a DataRowCollection object. 遍历DataRowCollection对象时,不应在foreach循环中调用Delete。 Delete modifies the state of the collection. 删除修改集合的状态。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM