[c#] Внутренний индекс DataTable поврежден



Answers

Лично эта особая ошибка была моей Немезидой в течение 3 недель в разных моделях. Я решил это в одной части моей базы кода, и она появляется в другом месте (я думаю, что я, наконец, раздавил ее сегодня). Информация об исключениях довольно бесполезна, и способ заставить reindex был бы приятной особенностью, учитывая отсутствие MS для решения проблемы.

Я бы не стал искать исправление MS - на нем есть статья KB, а затем перенаправляет вас на исправление ASP.Net, которое совершенно не связано.

Хорошо, достаточно жаловаться. Давайте посмотрим, что на самом деле помогло мне в решении этой конкретной проблемы в разных местах, где я столкнулся с ней:

  • Избегайте использования представлений по умолчанию и, если это возможно, измените представление по умолчанию. Btw, .Net 2.0 имеет ряд блокировок для чтения / записи при создании представлений, поэтому они не являются проблемой, которой они были до 2.0.
  • Вызовите AcceptChanges (), где это возможно.
  • Будьте осторожны. Выбирайте (выражение), поскольку в этом коде нет блокировки чтения / записи, и это единственное место (по крайней мере, согласно человеку на usenet, так что возьмите его с кусочком соли, однако , это очень похоже на вашу проблему, поэтому использование Mutexes может помочь)
  • Установите AllowDBNull в соответствующий столбец (сомнительное значение, но сообщается в usenet - я использовал его только в тех местах, где это имеет смысл)
  • Убедитесь, что вы не устанавливаете значение null (C #) / Nothing (VB) в поле DataRow. Используйте DBNull.Value вместо null. В вашем случае вы можете проверить, что поле не равно null, синтаксис выражения поддерживает оператор IsNull (val, alt_val).
  • Вероятно, это помогло мне (абсурдно, как кажется): если значение не меняется, не назначайте его. Поэтому в вашем случае используйте это вместо своего прямого назначения:

    if (column.Expression! = "some expression") column.Expression = "некоторое выражение";

(Я удалил квадратные скобки, не знаю, почему они были там).

Редактировать (5/16/12): просто наткнулся на эту проблему неоднократно (с помощью UltraGrid / UltraWinGrid). Использовал совет по удалению сортировки в DataView, а затем добавил отсортированный столбец, который соответствовал сортировке DataView, и это решило проблему.

Question

Я использую приложение .NET WinForms на C #, работающее с 3.5 .NET framework. В этом приложении я устанавливаю элемент .Expression в DataColumn в 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 не является многопоточным и работает только в потоке пользовательского интерфейса.

Я искал веб-страницы и форумы Microsoft , и есть много дискуссий и путаницы по этой проблеме. Назад, когда проблема впервые появилась в 2006 году, была мысль о том, что это был недостаток в платформе .NET, и были выпущены некоторые предполагаемые исправления, которые предположительно были перенесены в более поздние версии платформы .NET. Тем не менее, люди сообщают о смешанных результатах применения этих исправлений, которые больше не применимы к текущей структуре.

Другая распространенная теория заключается в том, что в DataTable есть операции, которые, хотя и кажутся безобидными, фактически являются операциями записи. Например, создание нового DataView на основе DataTable на самом деле является операцией записи в самой таблице, поскольку она создает внутренний индекс в DataTable для последующей ссылки. Эти операции записи не являются потокобезопасными, поэтому иногда случается, что условие гонки приводит к записи без потерь, совпадающей с нашим доступом к DataTable . Это, в свою очередь, приводит к повреждению внутреннего индекса DataTable , что приводит к исключению.

Я попытался установить блоки lock вокруг каждого создания DataView в коде, но, как я уже упоминал ранее, код, использующий DataTable , не имеет резьбы, и lock s не имела никакого эффекта, в любом случае.

Кто-нибудь видел это и успешно решал / работал вокруг него?

Нет, к сожалению, я не могу. Загрузка DataTable уже произошла к тому моменту, когда я удержал его, чтобы применить выражение к одному из его DataColumn. Я мог бы удалить столбец, а затем повторно добавить его, используя код, который вы предложили, но есть ли какая-то особая причина, по которой решение внутреннего индекса будет искажено?




Здесь же проблема, и попробовал другой подход. Я не использую datatable для любого связанного с экраном материала (например, привязки); Я просто создаю объекты 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);
         }
      }
   }
}



Вот что, похоже, сработало для моего коллеги Карен и Я. Мы получили эту ошибку в DataGridView, но только при вводе данных в один конкретный столбец.

Оказывается, я изменил порядок столбцов в сетке, не зная, что в элементе DataGridView.CellValidated есть код, который обнуляет значение в этом конкретном столбце, вызывающем проблему.

Этот код ссылался на конкретный номер столбца. Поэтому, когда исходный столбец 3 DataGridView был перемещен и стал столбцом 1, но код DataGridView.CellValidated все еще упоминается в столбце 3, произошла ошибка. Изменение нашего кода так, чтобы оно ссылалось на правильный e.ColumnIndex, похоже, устранило нашу проблему.

(Было нелегко понять, как изменить этот номер в нашем коде. Надеюсь, это исправление будет выполнено.)




У меня была та же проблема (индекс таблицы, поврежденный 5), при добавлении строк программно в набор данных, привязанный к datagridview. Я не принимал во внимание, что в событии AddRow в datagridview был обработчик событий, который выполняет некоторую инициализацию в случае, если пользователь запускает новую строку с помощью пользовательского интерфейса. В следах стека исключений ничего не было видно. Отключив событие, я смог быстро решить эту проблему. Я пришел к нему, только прочитав некоторые комментарии здесь глубоко. 2 часа не слишком много для таких вопросов :-), я думаю. Вы можете найти это, установив точку останова в каждом обработчике событий, назначенном datgridview, который связан с набором данных.




У меня была такая же проблема с использованием Threads. То, что я сделал, это создать делегат, который вызывается, когда мне нужно объединить таблицу.

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



Вот как я исправил свой внутренний индекс, это поврежденная проблема:

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; 

Наслаждайтесь, Андрей М




То же самое случилось со мной. Winforms, .NET 3.5, не удалось получить эту ошибку, пытаясь установить один из столбцов в типизированной строке. Код был довольно старым и долгое время работал, поэтому это было неприятное удивление ...

Мне нужно было установить новый SortNo в типизированной таблице TadaTable в наборе данных TadaSet.

Что помогло мне, вы также можете попробовать следующее:

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
}



Просто примечание для тех, кто пытается понять, как эту ошибку можно воспроизвести. У меня есть код, который довольно часто приводит к этой ошибке. Он блокирует одновременное чтение / запись, но вызов DataView.FindRows выполняется за пределами этой блокировки. ОП указал, что создание представления данных было скрытой операцией записи, также запрашивает ее?

//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, никаких исключений.




Related