我正在使用C#中的.NET WinForms应用程序,针对3.5 .NET框架运行。在这个应用程序中,我在DataColumn
中设置DataTable
的.Expression成员,如下所示:
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相关的代码不是多线程,只能在UI线程上运行。
我搜索过网络Microsoft forums,对此问题进行了大量讨论和混淆。回到2006年第一次报告这个问题的时候,人们认为它是.NET框架中的一个缺陷,并且有一些假定的修补程序可能已经发布到.NET框架的更高版本中。但是,人们报告在应用这些修补程序时出现了不同的结果,这些修补程序不再适用于当前框架。
另一个流行的理论是,DataTable上有一些操作,虽然看似无害,但实际上是写操作。例如,基于DataView
创建新的DataTable
实际上是对表本身的写操作,因为它在DataTable
中创建了一个内部索引供以后参考。这些写操作不是线程安全的,因此有时会发生竞争条件导致与我们访问DataTable
一致的非线程安全写入。反过来,这会导致DataTable
的内部索引损坏,从而导致异常。
我已尝试在代码中的每个lock
创建周围放置DataView
块,但正如我之前提到的,使用DataTable
的代码没有线程化,{{1}在任何情况下都无效。
有没有人见过这个并成功解决/解决过它?
不,不幸的是我做不到。加载DataTable已经发生,当我抓住它以将Expression应用于其中一个DataColumn时。我可以删除列,然后使用您建议的代码重新添加它,但是有一个特殊的原因可以解决内部索引是否已损坏的问题?
答案 0 :(得分:20)
我在导入行时遇到了同样的问题,因为看起来,在插入修复它之前调用DataTable.BeginLoadData
。
编辑:事实证明,这只是在一方修复它,现在添加行会抛出此异常。
Edit2:按照Robert Rossney的建议暂停绑定也为我解决了添加问题。我只是从DataSource
中删除了DataGridView
,并在完成DataTable
后对其进行了补充。
编辑3:仍然没有修复...自从星期四以来,我的代码中的异常一直在爬行...这是迄今为止最奇怪,最恐怖的错误到目前为止,我在框架中遇到过(我在使用.NET 2.0的3年里看到了许多奇怪的事情,足以保证我未来的项目不会在其上构建)。但足够的咆哮,回到主题。
我已经阅读了整个讨论at the Microsoft support forums,我将简要介绍一下。{3}}原bug report originates in '05。
不认真,这在我看来总结了一下。我能够从整个讨论中提取以下信息:
DataTable
不线程安全。如果您在其上的任何位置都有多线程,则必须自己Lock
/ Synchronize
。Expression
或已应用Sort
。DataTable.ListChanged()
事件,不要修改此事件中的数据或从中生成的任何事件。这包括来自绑定控件的不同Changed
事件。DefaultView
绑定到控件时可能会出现问题。始终使用DataTable.BeginLoadData()
和DataTable.EndLoadData()
。DefaultView
(及其DataTable
)上创建和操纵Index
是书写操作,Flying Spaghetti Monster知道原因。可能的来源很可能是竞争条件,无论是在我们的源代码中还是在框架的代码中。看起来,微软无法修复此错误或无意。无论哪种方式,检查您的代码是否有竞争条件,在我看来它与DefaultView
有关。在某些时候,Insert
或对数据的操纵正在破坏内部索引,因为更改未在整个DataTable
中正确传播。
当我发现更多信息或其他修复时,我当然会报告。对不起,如果我在这里有点情绪化,但我花了三天时间试图找出这个问题,并且它慢慢开始看起来是一个很好的理由去找一份新工作。
Edit4:我能够通过完全删除绑定(control.DataSource = null;
)并在完成数据加载后重新添加它来避免此错误。这让我觉得它与DefaultView
以及从绑定控件中产生的事件有关。
答案 1 :(得分:10)
就个人而言,这个特殊的错误已成为我各种时尚的3周克星。我已经在我的代码库的一部分解决了它,它出现在其他地方(我相信我终于在今晚压制它)。异常信息相当无益,并且由于缺少MS来解决问题,强制重新索引的方法将是一个很好的功能。
我不会寻找MS的修补程序 - 它们上面有一篇知识库文章,然后将你重定向到一个完全不相关的ASP.Net修复程序。
好的 - 足够的抱怨。让我们看看在我遇到过的各个地方解决这个问题的实际帮助我:
这可能对我有所帮助(听起来很荒谬):如果值没有改变,请不要分配它。因此,在您的案例中,请使用此代替您的直接分配:
if(column.Expression!=“some expression”)column.Expression =“some expression”;
(我删除了方括号,不知道他们为什么在那里)。
编辑(5/16/12):重复遇到此问题(使用UltraGrid / UltraWinGrid)。使用了删除DataView上的排序的建议,然后添加了一个与DataView排序匹配的排序列,这解决了这个问题。
答案 2 :(得分:3)
你提到“not threadsafe”。你是从不同的线程操纵对象吗?如果是这样,那很可能是腐败的原因。
答案 3 :(得分: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调用上,没有例外。
答案 4 :(得分:2)
我对这个问题的长期和痛苦的讨价还价的理解是,它是非线程安全写操作的工件,通常你不知道自己在做什么。
就我而言,罪魁祸首似乎是BindingSource。我发现我需要暂停绑定,执行我正在尝试的任何操作,然后在完成后恢复绑定,问题就消失了。这是18个月前,所以我不再清楚细节,但我记得BindingSource正在自己的线程上进行某种操作的印象。 (这对我来说现在比当时没有意义。)
另一个潜在的麻烦来源是DataTable的RowChanging事件。如果你做了一些修改该事件处理程序中的表的事情,那就要做坏事。
答案 5 :(得分: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);
}
}
}
}
答案 6 :(得分:1)
如何尝试将互斥量应用为described here 在这种情况下诱导线程睡眠?
答案 7 :(得分: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
答案 8 :(得分:1)
我在以编程方式向行绑定到datagridview的数据集添加行时遇到了同样的问题(表索引已损坏为5)。我没有考虑到datagridview的AddRow事件上有一个事件处理程序,如果用户通过UI启动新行,它会进行一些初始化。在异常堆栈跟踪中没有看到它。 通过禁用该事件,我可以快速解决这个问题。我只是通过深入阅读一些评论来实现它。对于这样的问题,2小时不是太多:-),我想。您可以通过在分配给与数据集链接的datgridview的每个事件处理程序中设置断点来找到它。
答案 9 :(得分: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 方法中,我在线程中进行了大量计算。
希望这有帮助。
答案 10 :(得分:1)
我通过这种方式解决了我的datatable-internal-index错误:
将CellEndEdit
更改为CellBeginEdit
个事件。另外......避免不必要地使用NULL:
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
答案 11 :(得分:0)
.NET 4.5.2 Win Forms应用程序具有相同的功能。 就我而言,原因似乎是绑定到单个BindingSource列的多个控件。我知道将许多控件绑定到一个值会带来麻烦,但是由于布局非常复杂,对我来说,这并不是一件容易的事。
似乎从一侧更改该值会引发多个OnChange事件,这些事件试图对BindingSource执行相同的操作,从而导致相关错误。
我尝试暂停绑定,但是没有成功。我已经实现了一些标志,可以防止代码并行执行多次,并且情况会更好。
答案 12 :(得分:0)
你不能只使用:
dtData.Columns.Add("TestColumn", typeof(Decimal), "Price * Quantity");
答案 13 :(得分:0)
我使用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);
答案 14 :(得分:0)
这似乎对我的同事Karen和我有用。我们在DataGridView中收到此错误,但仅在将数据输入到一个特定列时。
事实证明,我已经更改了网格中列的顺序,而不知道DataGridView.CellValidated子中的代码是否会导致该特定列中的值导致问题。
该代码引用了特定的列号。因此,当原始DataGridView列3被移动并成为第1列,但DataGridView.CellValidated代码仍然引用第3列时,发生了错误。更改我们的代码以便它引用正确的e.ColumnIndex似乎解决了我们的问题。
(要想在我们的代码中更改这一个数字并不容易。我希望这个解决方案成立。)
答案 15 :(得分: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事件。第一次当UI创建用于输入数据的新行时,第二次,我不确定,但看起来像将DataRowView添加到DataView时。
第一个“if”仅在第一次调用ItemAdded时起作用。在第二次调用时,“Foo”列已经填充并保持不变。
但是,可以在两个调用上执行“Bar”列默认代码。实际上在我的情况下它仅在第二个ItemAdded事件上执行,当用户有机会填写“Buz”列的数据时(最初“Buz”具有DBNull值)。
所以这是根据我的发现提出的建议:
e.ListChangedType == ListChangedType.ItemAdded
。DBNull.Value
等。)答案 16 :(得分:0)
可能是您在同一时间在多个进程中使用相同的数据表..我只是使用SYNCLOCK
来解决此问题...
试试这个..
SyncLock your datatable
'''' ----your datatable process
End SyncLock
答案 17 :(得分: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
}