Внутренний индекс DataTable поврежден - PullRequest
27 голосов
/ 16 января 2009

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

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

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

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

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


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

Ответы [ 17 ]

1 голос
/ 23 марта 2012

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

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 голосов
/ 02 мая 2013

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

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

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

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

0 голосов
/ 30 мая 2012

В моем случае версия 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.

Первое «если» работает только тогда, когда ItemAdded вызывается в первый раз. При втором вызове столбец «Foo» уже заполнен и оставлен как есть.

Однако код по умолчанию для столбца «Бар» может быть выполнен для обоих вызовов. На самом деле в моем случае это было выполнено только для второго события ItemAdded, когда у пользователя была возможность заполнить данные для столбца «Buz» (изначально «Buz» имеет значение DBNull).

Итак, вот рекомендации, основанные на моих выводах:

  • Данные в событии ListChanged могут быть изменены только при e.ListChangedType == ListChangedType.ItemAdded.
  • Перед установкой значения столбца необходимо выполнить проверку, чтобы убедиться, что это первое событие ItemAdded (например, если значение не может быть нулевым при втором вызове, проверьте, является ли оно DBNull.Value и т. Д.)
0 голосов
/ 20 апреля 2012

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

Попробуйте это ..

SyncLock your datatable

'''' ----your datatable process

End SyncLock
0 голосов
/ 22 ноября 2011

То же самое случилось со мной тоже. 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
}
0 голосов
/ 29 декабря 2017

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

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);
0 голосов
/ 16 января 2009

вы не можете просто использовать:

dtData.Columns.Add("TestColumn", typeof(Decimal), "Price * Quantity");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...