DataTable internal index is corrupted

前端 未结 18 1402
轻奢々
轻奢々 2020-12-04 15:55

I am working with a .NET WinForms app in C#, running against the 3.5 .NET framework. In this app, I am setting the .Expression member of a DataColumn in a

相关标签:
18条回答
  • 2020-12-04 16:33

    Just a note for those trying to see how this bug can be reproduced. I have some code which will fairly often produce that error. It does locking around concurrent read/writes, but the call to DataView.FindRows is done outside of that locking. The OP pointed out that creating a data view was a hidden write operation, is querying it one as well?

    //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
    

    If you run it as-is you could get output like this (output differs somewhat each time you run it):

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

    and if you do put the lock on the FindRows call, no exceptions.

    0 讨论(0)
  • 2020-12-04 16:33

    Same prob over here, and tried a different approach. I'm not using the datatable for any screen related stuff (e.g. binding); I'm just creating DataRow objects (in multiple threads) and adding them to the table.

    I've tried using lock(), and also tried centralizing the addition of the rows into a singleton, thinking this would help. It didn't. For reference, here's the singleton I used. Maybe someone else will be able to build on this and figure something out?

    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);
             }
          }
       }
    }
    
    0 讨论(0)
  • 2020-12-04 16:36

    My understanding, from long and painful haggling over this problem, is that it's an artifact of non-thread-safe write operations, which usually you didn't know you were making.

    In my case, the culprit seemed to be the BindingSource. I found that I needed to suspend binding, perform whatever operation I was trying, and then resume binding when I was done, and the problem went away. This was 18 months ago, so I'm not clear on the details anymore, but I remember getting the impression that the BindingSource is doing some kind of operation on its own thread. (This makes less sense to me now than it did at the time.)

    Another potential source of trouble is the DataTable's RowChanging event. If you do something that modifies the table in that event handler, expect bad things.

    0 讨论(0)
  • 2020-12-04 16:36

    Here is what seems to have worked for my colleague Karen and I. We were getting this error in a DataGridView, but only when entering data into one particular column.

    It turns out that I had changed the order of columns in the grid, not knowing that there was code in the DataGridView.CellValidated sub that nulls out the value in that one particular column causing the problem.

    That code referred to a specific column number. So when the original DataGridView column 3 was moved and became column 1, but the DataGridView.CellValidated code still referred to column 3, the error occurred. Changing our code so that it referred to the correct e.ColumnIndex seems to have fixed our problem.

    (It was not easy to figure out to change this one number in our code. I hope this fix holds.)

    0 讨论(0)
  • 2020-12-04 16:36

    May be you are using same datatable in mutiple process at same time.. I just resolved this issues using SYNCLOCK...

    Try this..

    SyncLock your datatable
    
    '''' ----your datatable process
    
    End SyncLock
    
    0 讨论(0)
  • 2020-12-04 16:37

    I've encountered the same issue and this is what fixed it for me: Stack Overflow - internal index is corrupted.

    If you are using threads with a dataset, that error will occur.

    In my case, I was trying to create a new row for a dataset within a method that was running in threads.

    One way was to use SyncLock around the method that creates the row or another way (and probably even better) was to create the rows outside of threads.

    Basically my code looks something like this:

        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)
    

    In the CreateElementRow method I'm doing a lot of calculations in threads.

    Hope this helps.

    0 讨论(0)
提交回复
热议问题