C# DataTable内部索引已损坏,要替换此默认对话框,请处理DataError事件

最近机器偶尔会报下面的错误,之前从来没有遇到过。

机器软体使用了一个DataGridView用来显示测量的数据,问题就出在这个控件上面。


image.png

(图1)


问题一:DataTable内部索引已损坏


解决的办法其实很简单,只要了解出错原因。

一般我们会使用DataTable.Select()方法来筛选数据,而DataTable会在执行Select()方法的时候系统会默认创建一个DataView,而DataView创建的时候会创建内部索引,如果这个时候数据被操作(添加,删除),那么内部索引就会被破坏,从而出错。大概是这个意思吧,不知道有没有说错。所以解决的方法也很简单,就是新建一个DataView,并在创建DataView的时候锁定DataTable。

DataView myDV;

lock(myTable)

{

    myDV=new DataView(myTable,"过滤","排序",DataViewRowState.CurrentRows);


    //数据操作

}


原文如下:


A DataTable is thread safe for read operations but not for write operations.


So this means you can store a DataTable in the cache and extract it and use it in a read only fashion and it will work fine.


However, creating a DataView on a DataTable is a write operation on a DataTable.  Most people don't know this, and its not very intuitive so I don't blame them for not knowing this.


What happens when you create a DataView on a DataTable is the DataView will create an index on the DataTable and this index is stored in the DataTable.  The reason for this is performance, for example if you create a DataView saying "F1=1" as the criteria, this creates an internal index on the DataTable to locate this information.  Later on if you create another DataView with the same criteria, the index is reused, so this improves performance.   However the fact that these indexes are stored inside the DataTable means that these are write operations to the DataTable and thus they are not thread safe.


So if you are creating random DataViews on the DataTable you are constantly creating new indexes.  If you are creating the same type of DataView over and over you are constantly reusing existing index.


So unfortunately you need to serialize the creating of DataViews over the DataTable.


You could do this for example to avoid the problem:


lock(myDataTableFromCache)

{

    dv = new DataView(myDataTableFromCache,...);

}


So lock using the DataTable as the locking object and lock when creating the DataView should solve the problem.


 下面是原文地址:http://forums.microsoft.com/MSDN/ShowPost.aspx?PostID=277680&SiteID=1&PageID=1



问题2:要替换此默认对话框,请处理DataError事件


网上普遍的解决方法是下面这样的:


 this.dgvState.DataError += delegate(object sender, DataGridViewDataErrorEventArgs e) { };

dgvState为DataGridView控件的名称

将上述代码添加到表单构造函数中即可屏蔽DataError 莫名其妙的提示信息了。

勇哥试验过这个方法确实是有效的!


如何让图1的错误再现呢?

见下面勇哥的测试代码。

在这段代码中,用了6,7个线程频繁的添加删除List<student>的项目,而另一个线程则做数据绑定显示数据List<student>到DataGridView控件。

这两件事同时做会有一定机率出现错误,下面的程序也许运不了几分钟就会报错。这个冲突的时间是随机的。

using sharClass;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace WindowsFormsApplication1
{
    public partial class Form1 : Form
    {
        List<student> data = new List<student>();
        Thread t1,t2,t3,t4,t5;
        
        public Form1()
        {
            InitializeComponent();

            this.myDataGridView1.DataError += myDataGridView1_DataError;

            for (int i = 0; i < 10; i++)
                data.Add(addNew());
            t1 = new Thread(new ThreadStart(fun1));
            t1.IsBackground = true;
            t2 = new Thread(new ThreadStart(fun2));
            t2.IsBackground = true;

            t3 = new Thread(new ThreadStart(fun3));
            t3.IsBackground = true;
            t4 = new Thread(new ThreadStart(fun4));
            t4.IsBackground = true;
            t5 = new Thread(new ThreadStart(fun5));
            t5.IsBackground = true;
        }

        void myDataGridView1_DataError(object sender, DataGridViewDataErrorEventArgs e)
        {

        }

        private void Form1_Load(object sender, EventArgs e)
        {
            
        }

        private void timer1_Tick(object sender, EventArgs e)
        {
            myDataGridView1.DataSource = null;
            myDataGridView1.DataSource = data;
            data.Add(addNew());
            
        }

        private void button1_Click(object sender, EventArgs e)
        {
            timer1.Enabled = true;
            timer1.Start();
            timer2.Enabled = true;
            timer2.Start();
            t1.Start();
            t2.Start();
            t3.Start();
            t4.Start();
            t5.Start();
        }

        private student addNew()
        {
            return new student() 
            { 
                name = randomHelper.GenerateRndNumLetter(8), 
               age = randomHelper.GetRandomInt(11,55),
                workAge = randomHelper.GetRandomInt(5555,9999) };
        }

        private void timer2_Tick(object sender, EventArgs e)
        {
            data.RemoveAt(randomHelper.GetRandomInt(0, data.Count - 1));
            data.Add(addNew());
            
        }

        private void fun1()
        {
            while(true)
            {
                if (randomHelper.GetRandomInt(1, 100) % 2 == 0)
                    data.RemoveAt(randomHelper.GetRandomInt(0, data.Count - 1));
                else
                    data.Add(addNew());
                Thread.Sleep(200);
            }
        }

        private void fun2()
        {
            while (true)
            {
                if (randomHelper.GetRandomInt(1, 100) % 2 == 0)
                    data.RemoveAt(randomHelper.GetRandomInt(0, data.Count - 1));
                else
                    data.Add(addNew());
                Thread.Sleep(200);
            }
        }

        private void fun3()
        {
            while (true)
            {
                if (randomHelper.GetRandomInt(1, 100) % 2 == 0)
                    data.RemoveAt(randomHelper.GetRandomInt(0, data.Count - 1));
                else
                    data.Add(addNew());
                Thread.Sleep(200);
            }
        }

        private void fun4()
        {
            while (true)
            {
                if (randomHelper.GetRandomInt(1, 100) % 2 == 0)
                    data.RemoveAt(randomHelper.GetRandomInt(0, data.Count - 1));
                else
                    data.Add(addNew());
                Thread.Sleep(200);
            }
        }

        private void fun5()
        {
            while (true)
            {
                if (randomHelper.GetRandomInt(1, 100) % 2 == 0)
                    data.RemoveAt(randomHelper.GetRandomInt(0, data.Count - 1));
                else
                    data.Add(addNew());
                Thread.Sleep(200);
            }
        }
    }

    public class student
    {
        public string name { get; set; }
        public object age { get; set; }
        public int workAge { get; set; }
    }
}


image.png


上面的演示程序中,如果没有下面的一句话,则启动程序后可能立刻就会报错误。见图2。

this.myDataGridView1.DataError += myDataGridView1_DataError;


image.png

(图2)


但是加上上面的代码后,这个默认的对话框就不再出现了!


--------------------- 

作者:hackpig
来源:www.skcircle.com
版权声明:本文为博主原创文章,转载请附上博文链接!


本文出自勇哥的网站《少有人走的路》wwww.skcircle.com,转载请注明出处!讨论可扫码加群:

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

会员中心
搜索
«    2024年4月    »
1234567
891011121314
15161718192021
22232425262728
2930
网站分类
标签列表
最新留言
    热门文章 | 热评文章 | 随机文章
文章归档
友情链接
  • 订阅本站的 RSS 2.0 新闻聚合
  • 扫描加本站机器视觉QQ群,验证答案为:halcon勇哥的机器视觉
  • 点击查阅微信群二维码
  • 扫描加勇哥的非标自动化群,验证答案:C#/C++/VB勇哥的非标自动化群
  • 扫描加站长微信:站长微信:abc496103864
  • 扫描加站长QQ:
  • 扫描赞赏本站:
  • 留言板:

Powered By Z-BlogPHP 1.7.2

Copyright Your skcircle.com Rights Reserved.

鄂ICP备18008319号


站长QQ:496103864 微信:abc496103864