2009-01-16 320 views
23

我正在使用C#中的.NET WinForms应用程序,针对3.5 .NET框架运行。在这个程序,我设置一个DataColumn的.Expression成员在DataTable,像这样:DataTable内部索引已损坏

DataColumn column = dtData.Columns["TestColumn"]; 
column.Expression = "some expression"; 

第二行,在那里我居然设置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) 

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

我搜查了网页和Microsoft forums,并且在这个问题上有很多讨论和混淆。回到2006年第一次报道该问题时,人们认为这是.NET框架中的一个缺陷,并且发布了一些推测的修补程序,这些修补程序大概会推出到.NET框架的更高版本中。但是,人们在应用那些不再适用于当前框架的修补程序时报告了各种各样的结果。

另一个流行的理论是DataTable上有操作,尽管看起来无害,但它实际上是写操作。例如,基于DataTable创建新的DataView实际上是对表本身的写操作,因为它在DataTable中创建了一个内部索引供以后参考。这些写入操作不是线程安全的,所以有时会发生竞态条件导致与我们访问DataTable相同的无线安全写入。这反过来会导致DataTable的内部索引损坏,导致例外。

我试图把lock块左右各DataView创作中的代码,但是,正如我前面提到,利用DataTable不是线程代码,和lock小号都没有效果,在任何情况下。

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


不,不幸的是我不能。加载DataTable的时候已经发生了,当我将它保存到一个DataColumn的表达式中。我可以删除列,然后重新添加它使用您建议的代码,但是有一个特定的原因,为什么这将解决内部索引已损坏的问题?

+0

实际是什么“有些表达”?你是否遵循http://tinyurl.com/7tj8av中的命名惯例? – balexandre 2009-01-16 20:37:40

+0

我遇到了同样的错误,这是修复它对我来说:[StackOverflow - 内部索引已损坏](http://stackoverflow.com/a/34154179/3119607) – mrc 2015-12-08 11:09:37

+0

此错误为我尝试时对数据集运行选择查询。它发生在搜索那里的东西时,并没有发现时,搜索的东西不是。我意识到当我创建一个新的数据行并添加它时,我没有锁定表,直到我将该行添加到表的最后一步。实际上,当我声明新行时不得不锁定它,直到将它添加到表中之后才释放锁。这与我发现错误的代码完全不同。 – Grungondola 2016-04-25 20:38:46

回答

0

你就不能使用:

dtData.Columns.Add("TestColumn", typeof(Decimal), "Price * Quantity"); 
3

你提到 “不是线程安全的”。你是从不同的线程操纵对象吗?如果是这样,那很可能是腐败的原因。

9

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

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

好 - 足够的抱怨。让我们来看看在我遇到它的各个地方解决这一具体问题实际上帮助了我:

  • 避免使用默认视图,并修改默认视图,如果可能的。顺便说一句,.Net 2.0在创建视图时有很多读写器锁定,所以它们不是它们在2.0之前的问题。
  • 尽可能地调用AcceptChanges()。
  • 请小心。选择(表达式),因为在这段代码中没有读写器锁 - 并且它是唯一的地方(至少根据usenet上的一个人,所以带上一粒盐 - 然而,这与你的问题非常相似 - 所以使用Mutexes可能会有所帮助)
  • 将AllowDBNull设置为问题列(可疑值,但在usenet上报告 - 我只在它使用它的地方使用它有意义)
  • 确保您没有将空(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排序相匹配的排序列,这解决了问题。

2

我的理解是,从长久而痛苦地讨论这个问题,它是非线程安全的写操作的工件,通常你不知道你在做什么。

在我的情况下,罪魁祸首似乎是BindingSource。我发现我需要暂停绑定,执行我正在尝试的任何操作,然后在完成时恢复绑定,并且问题消失。这是18个月前,所以我不清楚细节,但我记得有一个印象,即BindingSource正在自己的线程上进行某种操作。 (这比现在对我的意义不那么明显)。

另一个潜在的问题是DataTable的RowChanging事件。如果您在该事件处理程序中执行了修改该表的任何操作,那么应该预料到不好的情况

15

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

编辑:事实证明,这只是固定在一边,现在添加行引发此异常。

编辑2:暂停绑定如Robert Rossney建议,也为我解决了添加问题。我只是从DataGridView中删除DataSource,并在我完成DataTable后读取它。

EDIT3:还没定...除外一直以来周四在我的代码中的所有不同的地方爬起来......这是迄今为止最奇怪,最˚F****荷兰国际集团的错误我遇到到目前为止(在我使用.NET 2.0的三年中,我看到很多奇怪的事情,足以保证我的未来项目不会构建在其中)。但足够的咆哮,回到主题。

我已经通读了整个讨论at the Microsoft support forums,我会给你一个简短的总结。原来的bug report originates in '05

  • 三月'06:的Bug报告首次调查开始。在整个明年的过程中,它以不同形式和不同表现报道。
  • 三月'07:最后a hotfix with number KB 932491被释放(不要让你的希望),它链接对a download of an completely irrelevant looking hotfix,或者至少看起来是这样的。在接下来的几个月中,许多报告称修补程序不起作用,有些则报告成功。
  • July '07:来自微软的最后一个签名(带有完全无用的答案),超出这一点就没有微软的进一步回应。没有进一步的确认,没有尝试支持,没有更多信息的要求......没有。除此之外,只有社区相关信息。

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

  • DataTable线程安全的。如果您在其上的任何地方使用多线程,您将必须自行对其进行编程Lock/Synchronize
  • 索引的腐败发生在之前实际的异常被抛出。
  • 一个可能的损坏来源是应用的Expression或应用的Sort
  • 另一个可能的来源是DataTable.ListChanged()事件,决不会修改此事件中的数据或从中产生的任何事件。这包括来自绑定控件的不同Changed事件。
  • DefaultView与控件绑定时可能存在问题。始终使用DataTable.BeginLoadData()DataTable.EndLoadData()
  • DefaultView的创建和操作是写作操作DataTable(和它的Index),飞行意粉怪物知道为什么。

可能的原因很可能是竞争条件,无论是在我们的源代码还是在框架代码中。看起来,微软无法解决这个错误或无意。无论哪种方式,检查您的代码的竞争条件,这与我认为DefaultView有关。在某个点Insert或对数据的操纵会破坏内部索引,因为这些更改无法通过整个DataTable正确传播。

当然,如果我找到更多信息或其他修复方法,我当然会报告。对不起,如果我在这里有一点点情绪化,但我已花了三天的时间来确定这个问题,并慢慢开始看起来是一个很好的理由来找到一份新工作。

编辑4:我能够通过完全删除绑定(control.DataSource = null;)并在数据加载完成后重新添加它来避免此错误。这促使我认为它与DefaultView以及从绑定控件产生的事件有关。

+1

谢谢你鲍比。在我的BindingSource上移除Sort表达式为我解决了它。我将研究另一种排序数据的方法。 – Aranda 2011-08-23 07:38:45

1

同样的概率在这里,并尝试了不同的方法。我没有使用任何与屏幕相关的东西(例如绑定)的数据表;我只是创建DataRow对象(在多个线程中)并将它们添加到表中。

我试过使用lock(),并试图集中添加到单例中的行,认为这将有所帮助。它没有。作为参考,这里是我使用的单例。也许别人能够在这个基础上发展出一些东西呢?

using System; 
using System.Collections.Generic; 
using System.Linq; 
using System.Text; 
using System.Data; 

namespace EntityToDataSet 
{ 
    public class RowAdder 
    { 
     #region Data 
     private readonly object mLockObject = new object(); 
     private static RowAdder mInstance; 

     public static RowAdder Instance 
     { 
     get 
     { 
      if (mInstance == null) 
      { 
       mInstance = new RowAdder(); 
      } 
      return mInstance; 
     } 
     } 

     object mSync; 
     #endregion 

     #region Constructor 
     private RowAdder() 
     { 
     } 
     #endregion 

     public void Add(DataTable table, DataRow row) 
     { 
     lock (mLockObject) 
     { 
      table.Rows.Add(row); 
     } 
     } 
    } 
} 
1

如何试图将一个互斥体described here 诱导睡眠,在这样的条件下线程的想法?

0

我也是如此。 Winforms,.NET 3.5,试图在输入行中设置其中一列时,意外地发现了这个错误。代码是相当古老,工作了很长时间,所以这是一种不愉快的惊喜...

我需要在数据集TadaSet中的类型表TadaTable中设置新的SortNo。

什么帮助了我,你也可以试试这个:

int i = 0; 
foreach (TadaSet.TadaTableRow row in source) 
{ 
    row.BeginEdit(); //kinda magical operation but it helped :) 
    // Also you can make EndEdit() for each row later if you need... 
    short newNo = i++; 
    if (newNo != row.SortNo) row.SortNo = newNo; //here was the crash 
} 
1

这是我固定我的内部索引损坏问题:

System.Data.DataTable dtNew = new DataTable(); 
for (int iCol = 0; iCol < dtOriginalData.Columns.Count; iCol++) 
{ 
    dtNew.Columns.Add(dtOriginalData.Columns[iCol].ColumnName, dtOriginalData.Columns[iCol].DataType); 
} 
for (int iCopyIndex = 0; iCopyIndex < item.Data.Rows.Count; iCopyIndex++) 
{ 
    dtNew.Rows.Add(dtOriginalData.Rows[iCopyIndex].ItemArray); 
    //dtNew.ImportRow(dtOriginalData.Rows[iCopyIndex]); 
} 
dtOriginalData = dtNew; 

享受, 安德鲁中号

0

五月是你在同一时间在多个进程中使用相同的数据表..我刚刚解决这个问题使用SYNCLOCK ...

试试这个..

SyncLock your datatable 

'''' ----your datatable process 

End SyncLock 
0

在我的情况下Framework版本为2.0。 问题的根源在DataView ListChanged事件中。下面的代码用一些默认值初始化新行。

private void dataView_ListChanged(object sender, ListChangedEventArgs e) 
{ 
    if (e.ListChangedType == ListChangedType.ItemAdded) 
    { 
     DataView v = (DataView)sender; 
     DataRowView drv = v[e.NewIndex]; 

     // This "if" works fine 
     if (drv["Foo"] == DBNull.Value) 
     { 
      drv["Foo"] = GetFooDefault(); 
     } 

     // This "if" brakes the internal index  
     if (drv["Bar"] == DBNull.Value && drv["Buz"] != DBNull.Value) 
     { 
      drv["Bar"] = drv["Buz"]; 
     } 
    } 
} 

经过一番调查后,很明显ItemAdded事件每行至少调用两次。第一次当用户界面创建新线路输入数据,第二次,以及我不确定,但看起来像当DataRowView添加到DataView。

第一个“if”仅在第一次调用ItemAdded时有效。第二个电话“Foo”列已经填充并保持原样。

但是,“Bar”列的默认代码可以在两个调用中执行。实际上,在我的情况下,它仅在第二个ItemAdded事件中执行,当用户有机会填写“Buz”列的数据(最初“Buz”具有DBNull值)。

所以这里的建议是根据我的调查结果:

  • 在ListChanged事件的数据是可以改变的,只有当e.ListChangedType == ListChangedType.ItemAdded
  • 设置之前应进行检查,以确保这是第一个ItemAdded事件的列值(例如,如果值不能在第二个电话空,检查它是否是DBNull.Value等)
3

刚为那些试图看到这个错误如何被重现的人提供了一个笔记。我有一些相当经常会产生错误的代码。它锁定并发读/写,但对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 

如果你运行它,是你能得到这样的输出(输出有所不同,你运行它每次):

==============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 . . . 

,如果你真的把锁的FindRows打电话,没有例外。

0

下面是似乎已经为我的同事卡伦和我工作的东西。我们得到DataGridView中的这个错误,但只有当输入数据到一个特定的列。

事实证明,我已经改变了网格中列的顺序,并不知道DataGridView.CellValidated中有代码会导致出现问题的特定列中的值为零。

该代码引用了特定的列号。因此,当原始DataGridView列3移动并成为列1,但DataGridView.CellValidated代码仍然参考列3时,发生错误。更改我们的代码,以便它引用正确的e.ColumnIndex似乎已经解决了我们的问题。

(这是不容易弄清楚在我们的代码来改变这一个数字,我希望此修复程序持有。)

1

我解决了我的DataTable内部索引错误是这样的:

改变CellEndEditCellBeginEdit事件。此外...避免使用不必要的空值:

Private Sub User_role_groupDataGridView_CellBeginEdit(sender As Object, e As DataGridViewCellCancelEventArgs) Handles User_role_groupDataGridView.CellBeginEdit 
    Try 
     If Not Me.User_role_groupDataGridView.Rows(e.RowIndex).IsNewRow Then Me.User_role_groupDataGridView.Rows(e.RowIndex).Cells("last_modified_user_group_role").Value = Now 
    Catch ex As Exception 
     Me.displayUserMessage(ex.ToString, Me.Text, True) 
    End Try 
End Sub 
-1

我遇到了同样的问题,这是固定的对我来说:Stack Overflow - internal index is corrupted

如果您正在使用数据集的线程,则会发生该错误。

就我而言,我试图在线程中运行的方法中为数据集创建一个新行。

一种方法是使用SyncLock围绕创建行或其他方式(甚至可能更好)的方法是在线程之外创建行。

基本上我的代码看起来是这样的:

Dim elements As New List(Of element) 
    Dim dataRows As New List(Of MyDataSet.Row) 

    For i As Integer = 0 To elements.Count - 1 
     dataRows.Add(Me.Ds.Elements.NewElementsRow) 
    Next 

    Parallel.For(0, elements.Count, Sub(i As Integer) 
             Me.CreateElementRow(elements(i), dataRows(i)) 
            End Sub) 

CreateElementRow方法,我做了很多计算的线程。

希望这会有所帮助。

1

我有同样的问题(表索引损坏5)编程添加到绑定到datagridview的数据集的行。我没有考虑到datagridview的AddRow事件上有一个事件处理程序,它会在用户通过UI启动新行时进行一些初始化。在异常堆栈跟踪中没有看到它。 通过禁用事件我可以解决这个问题。我只是通过深入阅读一些评论才得出结论。 2小时没有太多的问题,如:-),我想。您可以通过在每个分配给datgridview的事件处理程序中设置断点来链接到数据集。

0

我有使用线程相同的问题。 我所做的是创建一个委托,当我需要合并表时调用它。

internal delegate void MergeData (DataTable dataTable1, DataTable dataTable2); 

internal static void MergeDataTable (DataTable dataTable1, DataTable dataTable2) 
{ 
    dataTable1.Merge (dataTable2, true); 
} 

然后在执行期间我调用委托并且不会发生错误。

Delegates.MergeData mergeData = new Delegates.MergeData (Delegates.MergeDataTable); 

object [] paramsMerge = {dataTable1, dataTable2}; 

this.Invoke (mergeData, paramsMerge);