use*_*525 27 .net c# datatable
我正在使用C#中的.NET WinForms应用程序,运行3.5 .NET框架.在这个应用程序中,我正在设置a DataColumn中的.Expression成员DataTable,如下所示:
DataColumn column = dtData.Columns["TestColumn"];
column.Expression = "some expression";
Run Code Online (Sandbox Code Playgroud)
我实际设置的第二行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)
Run Code Online (Sandbox Code Playgroud)
关于错误何时发生,没有明显的押韵或理由; 在加载相同的数据集时,它可能工作正常,但重新加载它将失败,反之亦然.这让我认为它与竞争条件有关,在DataTable我正在尝试修改其中一个列时,正在发生另一个写操作.但是,与DataTables 相关的代码不是多线程的,只能在UI线程上运行.
我搜索过网络和微软论坛,对这个问题有很多讨论和困惑.回到2006年第一次报告这个问题的时候,人们认为它是.NET框架中的一个缺陷,并且有一些假定的修补程序可能会被发布到.NET框架的更高版本中.但是,人们在应用这些修补程序时报告了不同的结果,这些修补程序不再适用于当前框架.
另一个流行的理论是DataTable上有一些操作,虽然看似无害,但实际上是写操作.例如,DataView基于a 创建新DataTable实际上是对表本身的写操作,因为它在DataTable以后的引用中创建了一个内部索引.这些写操作不是线程安全的,因此有时会发生竞争条件导致非线性安全写入与我们访问的时间相符DataTable.反过来,这会导致内部索引DataTable被破坏,从而导致异常.
我已经尝试在代码中lock围绕每个DataView创建放置块,但是,正如我之前提到的,使用的代码DataTable没有线程化,并且locks在任何情况下都没有效果.
有没有人看过这个并成功解决/解决它?
不,不幸的是我做不到.加载DataTable已经发生,当我抓住它以将Expression应用于其中一个DataColumn时.我可以删除列,然后使用您建议的代码重新添加它,但有一个特殊的原因,为什么这将解决内部索引已损坏的问题?
Bob*_*bby 20
我在导入行时遇到了同样的问题,因为看起来,DataTable.BeginLoadData在插入修复它之前调用它.
编辑:事实证明,这只是在一侧修复它,现在添加行会抛出此异常.
编辑2:罗伯特罗斯尼建议的暂停绑定也为我解决了添加问题.我只是删除了DataSource从DataGridView和重新添加它,我是用做后DataTable.
编辑3:仍然没有修复...自从星期四以来,我的代码中的异常一直在爬行...这是到目前为止我在框架中遇到的最奇怪和最常见的错误(在我使用.NET 2.0的3年里,我看到了许多奇怪的事情,足以保证我未来的一个项目都不会建立在它上面.但足够的咆哮,回到主题.
我已经阅读了Microsoft支持论坛上的整个讨论,我将简要介绍一下它.最初的错误报告起源于'05.
不认真,这在我看来总结了.我能够从整个讨论中提取以下信息:
DataTable是不是线程安全的.你必须Lock/ Synchronize在你自己,如果你有多线程上的任何地方.Expression或应用Sort.DataTable.ListChanged()事件,永远不要修改此事件中的数据或从中生成的任何事件.这包括Changed来自绑定控件的不同事件.DefaultView控件时可能会出现问题.总是使用DataTable.BeginLoadData()和DataTable.EndLoadData().DefaultView是对(及其)的写作操作,飞行意大利面怪物知道原因.DataTableIndex可能的来源很可能是竞争条件,无论是在我们的源代码中还是在框架的代码中.看起来,微软无法修复此错误或无意.无论哪种方式,检查你的代码的竞争条件,它与DefaultView我认为有关.在某些时候Insert,数据的操纵或操纵正在破坏内部索引,因为更改没有在整个过程中正确传播DataTable.
当我发现进一步的信息或其他修复时,我当然会报告.对不起,如果我在这里有点情绪化,但我花了三天时间试图找出这个问题,它慢慢开始看起来像是一个很好的理由去找一份新工作.
编辑4:我完全删除了绑定(control.DataSource = null;)并在完成数据加载后重新添加它,从而避免了这个错误.这促使我认为它与DefaultView从绑定控件产生的事件有关.
tor*_*ial 10
就个人而言,这个特殊的错误已成为我各种时尚的3周克星.我已经在我的代码库的一部分解决了它,它出现在其他地方(我相信我终于在今晚压制它).异常信息相当无益,并且由于缺少MS来解决问题,强制重新索引的方法将是一个很好的功能.
我不会寻找MS的修补程序 - 他们有一篇知识库文章,然后将你重定向到一个完全不相关的ASP.Net修复程序.
好的 - 足够的抱怨.让我们看看在我遇到过的各个地方解决这个问题的实际帮助我:
这可能对我帮助最大(听起来很荒谬):如果值没有改变,请不要分配它.所以在你的情况下使用它而不是你的直接任务:
if(column.Expression!="some expression")column.Expression ="some expression";
(我删除了方括号,不知道为什么他们在那里).
编辑(5/16/12):重复遇到这个问题(使用UltraGrid/UltraWinGrid).使用了删除DataView上的排序的建议,然后添加了一个与DataView排序匹配的排序列,这就解决了这个问题.
对于那些试图了解如何重现此错误的人来说,请注意。我有一些代码经常会产生该错误。它确实围绕并发读/写进行锁定,但对 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
Run Code Online (Sandbox Code Playgroud)
如果按原样运行它,您可能会得到如下输出(每次运行时输出都会有所不同):
==============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 . . .
Run Code Online (Sandbox Code Playgroud)
如果您确实将锁放在 FindRows 调用上,也不例外。
你不能只使用:
dtData.Columns.Add("TestColumn", typeof(Decimal), "Price * Quantity");
Run Code Online (Sandbox Code Playgroud)