DataTable内部索引已损坏

use*_*525 27 .net c# datatable

我正在使用C#中的.NET WinForms应用程序,运行3.5 .NET框架.在这个应用程序中,我正在设置a DataColumn中的.Expression成员DataTable,如下所示:

DataColumn column = dtData.Columns["TestColumn"];
column.Expression = "some expression";
Run Code Online (Sandbox Code Playgroud)

我实际设置的第二行Expression有时会导致以下异常:

FileName=
LineNumber=0
Source=System.Data
TargetSite=Int32 RBInsert(Int32, Int32, Int32, Int32, Boolean)
System.InvalidOperationException: DataTable internal index is corrupted: '5'.
   at System.Data.RBTree`1.RBInsert(Int32 root_id, Int32 x_id, Int32 mainTreeNodeID, Int32 position, Boolean append)
   at System.Data.RBTree`1.RBInsert(Int32 root_id, Int32 x_id, Int32 mainTreeNodeID, Int32 position, Boolean append)
   at System.Data.Index.InitRecords(IFilter filter)
   at System.Data.Index.Reset()
   at System.Data.DataTable.ResetInternalIndexes(DataColumn column)
   at System.Data.DataTable.EvaluateExpressions(DataColumn column)
   at System.Data.DataColumn.set_Expression(String value)
Run Code Online (Sandbox Code Playgroud)

关于错误何时发生,没有明显的押韵或理由; 在加载相同的数据集时,它可能工作正常,但重新加载它将失败,反之亦然.这让我认为它与竞争条件有关,在DataTable我正在尝试修改其中一个列时,正在发生另一个写操作.但是,与DataTables 相关的代码不是多线程的,只能在UI线程上运行.

我搜索过网络和微软论坛,对这个问题有很多讨论和困惑.回到2006年第一次报告这个问题的时候,人们认为它是.NET框架中的一个缺陷,并且有一些假定的修补程序可能会被发布到.NET框架的更高版本中.但是,人们在应用这些修补程序时报告了不同的结果,这些修补程序不再适用于当前框架.

另一个流行的理论是DataTable上有一些操作,虽然看似无害,但实际上是写操作.例如,DataView基于a 创建新DataTable实际上是对表本身的写操作,因为它在DataTable以后的引用中创建了一个内部索引.这些写操作不是线程安全的,因此有时会发生竞争条件导致非线性安全写入与我们访问的时间相符DataTable.反过来,这会导致内部索引DataTable被破坏,从而导致异常.

我已经尝试在代码中lock围绕每个DataView创建放置块,但是,正如我之前提到的,使用的代码DataTable没有线程化,并且locks在任何情况下都没有效果.

有没有人看过这个并成功解决/解决它?


不,不幸的是我做不到.加载DataTable已经发生,当我抓住它以将Expression应用于其中一个DataColumn时.我可以删除列,然后使用您建议的代码重新添加它,但有一个特殊的原因,为什么这将解决内部索引已损坏的问题?

Bob*_*bby 20

我在导入行时遇到了同样的问题,因为看起来,DataTable.BeginLoadData在插入修复它之前调用它.

编辑:事实证明,这只是在一侧修复它,现在添加行会抛出此异常.

编辑2:罗伯特罗斯尼建议的暂停绑定也为我解决了添加问题.我只是删除了DataSourceDataGridView和重新添加它,我是用做后DataTable.

编辑3:仍然没有修复...自从星期四以来,我的代码中的异常一直在爬行...这是到目前为止我在框架中遇到的最奇怪和最常见的错误(在我使用.NET 2.0的3年里,我看到了许多奇怪的事情,足以保证我未来的一个项目都不会建立在它上面.但足够的咆哮,回到主题.

我已经阅读了Microsoft支持论坛上的整个讨论,我将简要介绍一下它.最初的错误报告起源于'05.

  • 06年3月: Bug首次报道,调查开始.在整个明年的过程中,据报道它有不同的形式和不同的表现形式.
  • 2003年3月:最后发布了一个数字为KB 932491的修补程序(不要满怀期望),它链接下载一个完全不相关的外观修补程序,或者至少看起来如此.在接下来的几个月中,许多人报告说这个修补程序不起作用,有些报告成功.
  • 07年7月:微软的最后一次现场直播(完全无用的答案),超出这一点,微软没有做出进一步的回应.没有进一步的确认,没有尝试支持,没有更多信息的请求......没有.除此之外,只有社区相关信息.

不认真,这在我看来总结了.我能够从整个讨论中提取以下信息:

  • DataTable不是线程安全的.你必须Lock/ Synchronize在你自己,如果你有多线程上的任何地方.
  • 索引的损坏发生抛出实际异常之前的某个地方.
  • 一种可能的损坏源是应用Expression或应用Sort.
  • 另一个可能的来源是DataTable.ListChanged()事件,永远不要修改此事件中的数据或从中生成的任何事件.这包括Changed来自绑定控件的不同事件.
  • 绑定DefaultView控件时可能会出现问题.总是使用DataTable.BeginLoadData()DataTable.EndLoadData().
  • 创作和操纵DefaultView是对(及其)的写作操作,飞行意大利面怪物知道原因.DataTableIndex

可能的来源很可能是竞争条件,无论是在我们的源代码中还是在框架的代码中.看起来,微软无法修复此错误或无意.无论哪种方式,检查你的代码的竞争条件,它与DefaultView我认为有关.在某些时候Insert,数据的操纵或操纵正在破坏内部索引,因为更改没有在整个过程中正确传播DataTable.

当我发现进一步的信息或其他修复时,我当然会报告.对不起,如果我在这里有点情绪化,但我花了三天时间试图找出这个问题,它慢慢开始看起来像是一个很好的理由去找一份新工作.

编辑4:我完全删除了绑定(control.DataSource = null;)并在完成数据加载后重新添加它,从而避免了这个错误.这促使我认为它与DefaultView从绑定控件产生的事件有关.


tor*_*ial 10

就个人而言,这个特殊的错误已成为我各种时尚的3周克星.我已经在我的代码库的一部分解决了它,它出现在其他地方(我相信我终于在今晚压制它).异常信息相当无益,并且由于缺少MS来解决问题,强制重新索引的方法将是一个很好的功能.

我不会寻找MS的修补程序 - 他们有一篇知识库文章,然后将你重定向到一个完全不相关的ASP.Net修复程序.

好的 - 足够的抱怨.让我们看看在我遇到过的各个地方解决这个问题的实际帮助我:

  • 避免使用默认视图,并尽可能修改默认视图.顺便说一句,.Net 2.0在创建视图时有许多读/写锁,因此它们不是2.0之前的问题.
  • 尽可能调用AcceptChanges().
  • 注意.选择(表达式),因为这段代码中没有读取器/写入器锁定 - 而且它是唯一的地方(至少根据usenet上的人来说,所以拿它来做一粒盐 - 但是,这与您的问题非常相似 - 因此使用互斥锁可能有所帮助)
  • 将AllowDBNull设置为有问题的列(可疑值,但在usenet上报告 - 我只在有意义的地方使用它)
  • 确保您没有将null(C#)/ Nothing(VB)设置为DataRow字段.使用DBNull.Value而不是null.在您的情况下,您可能希望检查该字段是否为空,表达式语法确实支持IsNull(val,alt_val)运算符.
  • 这可能对我帮助最大(听起来很荒谬):如果值没有改变,请不要分配它.所以在你的情况下使用它而不是你的直接任务:

    if(column.Expression!="some expression")column.Expression ="some expression";

(我删除了方括号,不知道为什么他们在那里).

编辑(5/16/12):重复遇到这个问题(使用UltraGrid/UltraWinGrid).使用了删除DataView上的排序的建议,然后添加了一个与DataView排序匹配的排序列,这就解决了这个问题.


Ans*_*sss 5

对于那些试图了解如何重现此错误的人来说,请注意。我有一些代码经常会产生该错误。它确实围绕并发读/写进行锁定,但对 DataView.FindRows 的调用是在该锁定之外完成的。OP指出创建数据视图是一种隐藏的写入操作,查询它也是一种隐藏的写入操作吗?

//based off of code at http://support.microsoft.com/kb/932491
using System.Data;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using System;
public class GenerateSomeDataTableErrors
{   
    public static void Main()
    {
        DataTable Table = new DataTable("Employee");
        Table.Columns.Add("Id", typeof(int));
        Table.Columns.Add("Name", typeof(string));
        Table.PrimaryKey = new DataColumn[] { Table.Columns["Id"] };

        DataSet Employees = new DataSet();
        Employees.Tables.Add(Table);

        DataRow ManagerB = Table.NewRow();
        ManagerB["ID"] = 392;
        ManagerB["Name"] = "somename";
        Table.Rows.Add(ManagerB);

        DataRow ManagerA = Table.NewRow();
        ManagerA["ID"] = 394;
        ManagerA["Name"] = "somename";
        Table.Rows.Add(ManagerA);

        Employees.AcceptChanges();

        object locker = new object();

        //key = exception string, value = count of exceptions with same text
        ConcurrentDictionary<string, int> exceptions = new ConcurrentDictionary<string, int>();

        DataView employeeNameView = new DataView(Table, string.Empty, "Name", DataViewRowState.CurrentRows);

        Parallel.For(0, 100000, (i, s) =>
        {
            try
            {
                #region do modifications to the table, in a thread-safe fashion
                lock (locker)
                {
                    var row = Table.Rows.Find(392);

                    if (row != null) //it's there, delete it
                    {
                        row.Delete();
                        Employees.AcceptChanges();
                    }
                    else //it's not there, add it
                    {
                        var newRow = Table.NewRow();
                        newRow["ID"] = 392;
                        newRow["Name"] = "somename";
                        Table.Rows.Add(newRow);
                        Employees.AcceptChanges();
                    }
                }
                #endregion

                //Apparently this is the dangerous part, finding rows 
                // without locking on the same object the modification work is using.
                //lock(locker)
                employeeNameView.FindRows("somename");
            }
            catch (Exception e)
            {
                string estring = e.ToString();
                exceptions.TryAdd(estring, 0);
                lock (exceptions)
                { exceptions[estring] += 1; }
            }
        });

        foreach (var entry in exceptions)
        {
            Console.WriteLine("==============The following occurred " + entry.Value + " times");
            Console.WriteLine(entry.Key);
        }
    }//Main
}//class
Run Code Online (Sandbox Code Playgroud)

如果按原样运行它,您可能会得到如下输出(每次运行时输出都会有所不同):

==============The following occurred 2 times
System.InvalidOperationException: DataTable internal index is corrupted: '13'.
   at System.Data.RBTree`1.GetNodeByIndex(Int32 userIndex)
   at System.Data.DataView.GetRow(Int32 index)
   at System.Data.DataView.GetDataRowViewFromRange(Range range)
   at System.Data.DataView.FindRowsByKey(Object[] key)
   at GenerateSomeDataTableErrors.<>c__DisplayClass9.<Main>b__8(Int32 i, ParallelLoopState s) in Program.cs:line 110
==============The following occurred 3 times
System.IndexOutOfRangeException: Index 1 is either negative or above rows count.
   at System.Data.DataView.GetRow(Int32 index)
   at System.Data.DataView.GetDataRowViewFromRange(Range range)
   at System.Data.DataView.FindRowsByKey(Object[] key)
   at GenerateSomeDataTableErrors.<>c__DisplayClass9.<Main>b__8(Int32 i, ParallelLoopState s) in line 110
==============The following occurred 1 times
System.NullReferenceException: Object reference not set to an instance of an object.
   at System.Data.DataView.GetRow(Int32 index)
   at System.Data.DataView.GetDataRowViewFromRange(Range range)
   at System.Data.DataView.FindRowsByKey(Object[] key)
   at GenerateSomeDataTableErrors.<>c__DisplayClass9.<Main>b__8(Int32 i, ParallelLoopState s) in Program.cs:line 110
Press any key to continue . . .
Run Code Online (Sandbox Code Playgroud)

如果您确实将锁放在 FindRows 调用上,也不例外。


bal*_*dre 0

你不能只使用:

dtData.Columns.Add("TestColumn", typeof(Decimal), "Price * Quantity");
Run Code Online (Sandbox Code Playgroud)