31

私は、C# で .NET WinForms アプリを使用して、3.5 .NET フレームワークに対して実行しています。このアプリでは、 a の .Expression メンバーを次のように設定してDataColumnDataTableます。

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

実際に を設定した 2 行目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これは、列の1つを変更しようとしているときに別の書き込み操作が発生している競合状態に関連していると思われます。DataTableただし、 s に関連するコードはマルチスレッド化されておらず、UI スレッドでのみ実行されます

Web およびMicrosoft フォーラムを検索しましたが、この問題について多くの議論と混乱が生じています。この問題が最初に報告された 2006 年には、これは .NET フレームワークの欠陥であると考えられており、.NET フレームワークの新しいバージョンに組み込まれたと思われる修正プログラムがいくつかリリースされていました。ただし、現在のフレームワークには適用できなくなったこれらの修正プログラムを適用した結果がまちまちであると報告されています。

もう 1 つの有力な理論は、DataTable には操作があり、一見無害に見えますが、実際には書き込み操作であるというものです。たとえば、 にDataView基づいて新しい を作成することは、後で参照DataTableするために に内部インデックスを作成するため、実際にはテーブル自体に対する書き込み操作です。DataTableこれらの書き込み操作はスレッド セーフではないため、DataTable. これにより、の内部インデックスがDataTable破損し、例外が発生します。

lockコード内の各作品の周りにブロックを配置しようとDataViewしましたが、前述のように、 を使用するコードDataTableはスレッド化されlockておらず、いずれにせよ s は効果がありませんでした。

誰かがこれを見て、うまく解決/回避しましたか?


いいえ、残念ながらできません。DataTable のロードは、DataColumn の 1 つに Expression を適用するために DataTable を取得するまでに既に発生しています。列を削除してから、提案されたコードを使用して再度追加することはできますが、内部インデックスが破損しているという問題を解決する特定の理由はありますか?

4

18 に答える 18

23

行のインポート中に同じ問題が発生したようですがDataTable.BeginLoadData、挿入が修正される前に呼び出しました。

編集:結局のところ、これは片側でのみ修正され、行を追加するとこの例外がスローされるようになりました。

Edit2: Robert Rossneyが提案したようにバインドを一時停止すると、追加の問題も修正されました。を から削除DataSourceし、DataGridViewを使い終わった後に再度追加しましたDataTable

Edit3:まだ修正されていません... 例外は、木曜日以降、私のコードのすべての異なる場所に忍び寄っています... これは、これまでにフレームワークで遭遇した中で最も奇妙で最も厄介なバグです (そして、私が .NET 2.0 に取り組んできた 3 年間で、多くの奇妙なことを見てきました。これは、私の将来のプロジェクトが 1 つも .NET 2.0 で構築されないことを保証するのに十分です)。しかし、話題に戻ります。

Microsoft サポート フォーラムでの議論全体を読んだので、その簡単な要約を示します。オリジナルのバグ レポートは '05 年に作成されました

  • 2006 年 3 月:バグが初めて報告され、調査が開始されました。翌年を通して、それはさまざまな形や症状で報告されます。
  • 2007 年 3 月:最後に、番号 KB 932491 のホットフィックスがリリースされました (期待しないでください)。これは、まったく無関係に見えるホットフィックスのダウンロードにリンクするか、少なくともそう思われます。次の数か月間、ホットフィックスが機能しないという報告が多数寄せられましたが、成功したという報告もありました。
  • 2007 年 7 月: Microsoft からのライブの最後の兆候 (完全に役に立たない回答)。これ以降、Microsoft からの応答はありません。それ以上の確認も、サポートの試みも、詳細情報の要求もありません...何もありません。このポイントを超えると、コミュニティ関連の情報のみが表示されます。

真剣に、これは私の意見でそれを要約します。議論全体から次の情報を抽出することができました。

  • はスレッドセーフDataTableではありません。どこかにマルチスレッディングがある場合は、 / それを自分で行う必要がありますLockSynchronize
  • インデックスの破損は、実際の例外がスローされる前のどこかで発生します。
  • 考えられる破損の原因の 1 つは、適用済みExpressionまたは適用済みのいずれかSortです。
  • 別の原因として考えられるのはDataTable.ListChanged()イベントです。このイベントまたはそれから発生するイベントのデータは決して変更しないでください。これにはChanged、バインドされたコントロールからのさまざまなイベントが含まれます。
  • DefaultViewコントロールに対して をバインドするときに、問題が発生する可能性があります。常に と を使用DataTable.BeginLoadData()DataTable.EndLoadData()ます。
  • の作成と操作は(およびその)DefaultViewに対する書き込み操作であり、フライング スパゲッティ モンスターはその理由を知っています。DataTableIndex

これの原因として考えられるのは、ソース コードまたはフレームワークのコードの競合状態である可能性が最も高いです。どうやら、Microsoft はこのバグを修正できないか、修正するつもりがないようです。DefaultViewいずれにせよ、競合状態についてコードを確認してください。私の意見では、それは何かに関係しています。ある時点Insertで、データの操作によって内部インデックスが破損します。これは、変更が全体に適切に反映されないためDataTableです。

もちろん、さらなる情報や追加の修正が見つかったら報告します。ここで少し感情的になったら申し訳ありませんが、この問題を特定するのに 3 日間を費やしましたが、新しい仕事に就く正当な理由のようにゆっくりと見えてきました。

Edit4:control.DataSource = null;バインディング ( ) を完全に削除し、データのロードが完了した後に再度追加することで、このバグを回避できました。DefaultViewこれは、バインドされたコントロールから生成される および イベントと関係があるという私の考えを助長します。

于 2011-04-14T15:26:26.070 に答える
11

個人的には、この特定のバグは、さまざまな形で 3 週間にわたって私の宿敵でした。コードベースの一部で解決しましたが、他の場所にも表示されます(今夜、最終的に押しつぶしたと思います)。例外情報は役に立たず、問題を解決するための MS がないことを考えると、再インデックスを強制する方法は優れた機能でした。

MS の修正プログラムは探しません。KB 記事があり、まったく関係のない ASP.Net 修正プログラムにリダイレクトされます。

わかりました-十分に不平を言います。私が遭遇したさまざまな場所で、この特定の問題を解決するのに実際に何が役立ったかを見てみましょう。

  • デフォルト ビューの使用は避け、可能であればデフォルト ビューを変更します。ところで、.Net 2.0 には、ビューの作成時に多数のリーダー/ライター ロックがあるため、2.0 より前の問題ではありません。
  • 可能であれば AcceptChanges() を呼び出します。
  • .Select(expression) には注意してください。このコードにはリーダー/ライター ロックがないためです。これが唯一の場所です (少なくとも、usenet のユーザーによると、多少の注意を払ってください。ただし、 、これはあなたの問題と非常に似ています-したがって、ミューテックスを使用すると役立つ場合があります)
  • AllowDBNull を問題の列に設定します (疑わしい値ですが、usenet で報告されています。意味のある場所でのみ使用しました)。
  • null (C#)/Nothing (VB) を DataRow フィールドに設定していないことを確認してください。null の代わりに DBNull.Value を使用します。あなたの場合、フィールドが null でないことを確認したい場合があります。式の構文はIsNull (val, alt_val) 演算子をサポートしています。
  • これはおそらく私を最も助けてくれました (ばかげているように聞こえます): 値が変更されていない場合は、割り当てないでください。したがって、あなたの場合、完全な割り当ての代わりにこれを使用してください:

    if (column.Expression != "何らかの式") column.Expression = "何らかの式";

(角かっこを削除しましたが、なぜそこにあったのかわかりません)。

編集 (2012 年 5 月 16 日): この問題に繰り返し遭遇しました (UltraGrid/UltraWinGrid を使用)。DataView で並べ替えを削除するというアドバイスを使用してから、DataView の並べ替えに一致する並べ替えられた列を追加すると、問題が解決しました。

于 2009-01-29T05:22:27.987 に答える
4

「スレッドセーフではない」と述べています。異なるスレッドからオブジェクトを操作していますか? もしそうなら、それが腐敗の理由である可能性が非常に高い.

于 2009-01-17T16:19:26.887 に答える
4

このバグをどのように再現できるかを確認しようとしている人へのメモです。私はかなり頻繁にそのエラーを生成するいくつかのコードを持っています。同時読み取り/書き込みをロックしますが、DataView.FindRows への呼び出しはそのロックの外で行われます。OPは、データビューの作成は非表示の書き込み操作であると指摘しましたが、クエリも1つですか?

//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 呼び出しにロックをかけた場合、例外はありません。

于 2012-08-14T18:26:13.840 に答える
2

この問題をめぐる長くてつらい議論から、これは非スレッドセーフな書き込み操作のアーティファクトであると私は理解しています。

私の場合、犯人は BindingSource のようです。バインドを一時停止し、試行中の操作をすべて実行し、完了したらバインドを再開する必要があることがわかり、問題は解決しました。これは 18 か月前のことなので、詳細はわかりませんが、BindingSource が独自のスレッドで何らかの操作を行っているような印象を受けたことを覚えています。(これは、当時よりも今では意味がありません。)

もう 1 つの潜在的な問題の原因は、DataTable の RowChanging イベントです。そのイベント ハンドラーでテーブルを変更する何かを行う場合は、悪いことが予想されます。

于 2009-03-19T18:18:18.817 に答える
1

このような状況でスレッドにスリープを誘導するために、ここで説明するミューテックスを適用するというアイデアを試してみませんか?

于 2011-06-03T10:10:21.623 に答える
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);
         }
      }
   }
}
于 2011-05-18T20:53:05.110 に答える
1

データグリッドビューにバインドされているデータセットにプログラムで行を追加する際に、同じ問題 (テーブル インデックスが 5 で破損) がありました。ユーザーがUIによって新しい行を開始した場合に備えて、いくつかの初期化を行うdatagridviewのAddRowイベントにイベントハンドラーがあったことを考慮しませんでした。例外スタック トレースでは、何も見られませんでした。イベントを無効にすることで、これをすばやく解決できました。ここでいくつかのコメントを深く読んだだけで、私はそれにたどり着きました。そのような問題には2時間もかかりません:-)、と思います。これは、データセットにリンクされている datgridview に割り当てられたすべてのイベント ハンドラにブレークポイントを設定することで見つけることができます。

于 2016-02-21T00:37:06.090 に答える
1

私は同じ問題に遭遇しましたが、これが私のためにそれを修正したものです: Stack Overflow - internal index is壊れています

データセットでスレッドを使用している場合、そのエラーが発生します。

私の場合、スレッドで実行されているメソッド内でデータセットの新しい行を作成しようとしていました。

1 つの方法は、行を作成するメソッドの周りで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メソッドでは、スレッドで多くの計算を行っています。

お役に立てれば。

于 2015-12-08T11:12:06.960 に答える
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; 

お楽しみください、アンドリュー・M

于 2012-03-23T09:08:50.143 に答える
0

私にも同じことが起こりました。Winforms、.NET 3.5 は、入力された行の列の 1 つを設定しようとして、予期せずこのエラーを受け取りました。コードはかなり古く、長い間機能していたので、ちょっと不愉快な驚きでした...

データセット 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
}
于 2011-11-22T12:14:45.640 に答える
0

私の場合、フレームワークのバージョンは 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 イベントが行ごとに少なくとも 2 回呼び出されることが明らかになりました。UI が最初にデータを入力するための新しい行を作成したときと 2 回目はよくわかりませんが、DataRowView が DataView に追加されたときのように見えます。

最初の「if」は、ItemAdded が初めて呼び出されたときにのみ機能します。2 番目の呼び出しでは、「Foo」列は既に入力されており、そのまま残されます。

ただし、「Bar」列のデフォルト設定コードは両方の呼び出しで実行できます。実際、私の場合、ユーザーが「Buz」列のデータを入力する機会があった2番目のItemAddedイベントでのみ実行されました(最初は「Buz」にはDBNull値があります)。

そこで、私の調査結果に基づいた推奨事項を次に示します。

  • ListChanged イベントのデータは、 の場合にのみ変更できますe.ListChangedType == ListChangedType.ItemAdded
  • 列の値を設定する前に、これが最初の ItemAdded イベントであることを確認するためにチェックを実行する必要があります (たとえば、2 回目の呼び出しで値を null にすることができない場合は、そうであるかどうかを確認しますDBNull.Value)。
于 2012-05-30T08:33:05.597 に答える
0

複数のプロセスで同時に同じデータテーブルを使用している可能性があります..私はこの問題を解決しましたSYNCLOCK...

これを試して..

SyncLock your datatable

'''' ----your datatable process

End SyncLock
于 2012-04-20T13:33:53.203 に答える
0

あなたはただ使うことができません:

dtData.Columns.Add("TestColumn", typeof(Decimal), "Price * Quantity");
于 2009-01-16T20:50:49.290 に答える
0

これは、同僚のカレンと私にとってうまくいったようです。DataGridView でこのエラーが発生していましたが、特定の列にデータを入力した場合のみでした。

問題を引き起こしている特定の列の値を無効にするコードが DataGridView.CellValidated サブにあることを知らずに、グリッド内の列の順序を変更したことがわかりました。

そのコードは特定の列番号を参照していました。そのため、元の DataGridView 列 3 が移動されて列 1 になったが、DataGridView.CellValidated コードがまだ列 3 を参照していると、エラーが発生しました。正しい e.ColumnIndex を参照するようにコードを変更すると、問題が解決したようです。

(私たちのコードでこの 1 つの数値を変更することを理解するのは簡単ではありませんでした。この修正が適用されることを願っています。)

于 2013-05-01T20:15:44.770 に答える