“收藏被修改;枚举操作可能不会在System.Data.TypedTableBase <>中执行

时间:2019-04-09 15:30:26

标签: c# datatable datarowcollection

我有一个.NET 4.0程序集;它已在GAC中注册,并且是BizTalk“业务流程”的一部分。 有时会出现以下错误-“集合已修改;枚举操作可能无法执行。 :System.InvalidOperationException:集合已修改;枚举操作可能无法执行。”。我无法复制它;当我对相同数据执行相同处理时,我的程序集在该位置不会产生错误。

当我为数据表对象(分类为System.Data.TypedTableBase的对象)调用“ .Where()。ToArray()”时,会发生错误。

这是代码: ..................

int? setTypeGroupId;
...

return instances.WorkContributors.Where
    (
        c =>
            !c.IsInterestedPartyNoNull()
            && c.InterestedPartyNo == publisherIpNo
            && c.SetTypeNo == 1
            && (c.RecordType == "SPU")
        && c.TypeCode == "E" 
            && (!setTypeGroupId.HasValue ||  
            (setTypeGroupId.HasValue && c.SetTypeGroupID == setTypeGroupId))
    ).ToArray();
..................

“ instances”对象是一个数据集-我的类是从System.Data.DataSet生成的。 “ instances.WorkContributors”属性是一个数据表:System.Data.TypedTableBase类的对象。 MyDataRowClass类是从System.Data.DataRow生成的。

错误后的调用堆栈如下: 集合已修改;枚举操作可能无法执行。 :System.InvalidOperationException:集合已修改;枚举操作可能无法执行。 在System.Data.RBTree 1.RBTreeEnumerator.MoveNext() at System.Linq.Enumerable.<CastIterator>d__97 1.MoveNext() 在System.Linq.Enumerable.WhereEnumerableIterator 1.MoveNext() at System.Linq.Buffer 1..ctor(IEnumerable 1 source) at System.Linq.Enumerable.ToArray[TSource](IEnumerable 1来源)处 在MyProduct.FileParser.Types.CWR.PWRType.GetPublishers(CWRWorkInstances实例,可为1 setTypeGroupId) 在MyProduct.FileParser.Validation.Concreate.PwrTypeValidation.ValidatePublisherNumber() 在MyProduct.FileParser.Validation.Concreate.PwrTypeValidation.Validate() 在MyProduct.FileParser.Types.CWR.PWRType.StoreRecord(CWRWorkInstances workInstances,CWRWorkParsingContext上下文) 在MyProduct.FileParser.Groups.CWR.NWRGroup.StoreGroup(Int32 workBatchID,CWRFileCommonData commonData) 在MyProduct.FileParser.CWRParser.ProcessCWRFile(String fileName,Boolean wait,Boolean deleteFile,String sourceFileName)

我不明白为什么会发生错误;以及为什么它仅在某些情况下发生,而不会在相同的处理数据上再次发生。 错误“集合已修改;枚举操作可能不会执行。但我不明白为什么在我的代码中会发生这种情况。如果这样的代码,将排除该错​​误:

foreach (DataRow currRow in _someDataTable.Rows)
{
    if (/*deletion condition*/)
    {
        someDataTable.Rows.Remove(currRow);
    }
}

但是我上面的代码只想枚举System.Data.TypedTableBase并将结果转换为数组。

有什么想法吗?

2 个答案:

答案 0 :(得分:0)

将.ToArray()更改为.ToList()。两者之间存在语义差异,the answer here

可以很好地说明
  

另外两个(好的)答案集中在微观上   将会发生的性能差异。这篇文章只是   补充提到在语义之间存在的差异   与返回的数组相比,由数组(T [])生成的IEnumerator   通过列表。最佳示例说明:

 IList<int> source = Enumerable.Range(1, 10).ToArray();  // try changing to .ToList()

 foreach (var x in source) {   if (x == 5)
    source[8] *= 100;   Console.WriteLine(x); } 
The above code will run with no exception and produces the output: 
1 
2 
3 
4 
5 
6 
7 
8 
900 
10
  

这表明int []返回的IEnumarator没有   跟踪自创建以来是否已修改数组   枚举数请注意,我将局部变量source声明为   一个IList。这样,我确保C#编译器不会   将foreach语句优化为等效于   for(var idx = 0; idx 1[System.Int32] but of course this is an implementation detail. Now, if I change .ToArray() into .ToList(), I get only: 1 2 3 4 5 followed by a System.InvalidOperationException blow-up saying: Collection was modified; enumeration operation may not execute. The underlying enumerator in this case is the public mutable value-type System.Collections.Generic.List 1 + Enumerator [System.Int32](带框   在这种情况下,则是在IEnumerator框内,因为我使用了IList)。   总之,由List产生的枚举数保持不变   列表在枚举期间是否更改,而枚举器   T []产生的结果不是。所以在选择时要考虑这种差异   在.ToList()和.ToArray()之间。人们通常会多加一   .ToArray()或.ToList()绕过跟踪的集合   是否在枚举器的生存期内对其进行了修改。 (如果   任何人都想知道List <>如何跟踪收集是否   被修改,此类中有一个私有字段_version   每次List <>更新时更改。)

答案 1 :(得分:0)

在使用foreach遍历集合时,您无法修改集合。 复制并从副本中删除。

 DataTable Junk = new DataTable();
 foreach (DataRow currRow in _someDataTable.Rows)
 {
     if (/*deletion condition*/)
     {
         Junk.Add(currRow);
     }
 }
 foreach (DataRow row in Junk)
 {
    _ someDataTable.Rows.REmove(row);
 }