«Коллекция была изменена;операция перечисления может не выполняться »внутри System.Data.TypedTableBase <> - PullRequest
0 голосов
/ 09 апреля 2019

У меня сборка .NET 4.0; он зарегистрирован в GAC и работает как часть «оркестровки» BizTalk. Иногда я получаю следующую ошибку: «Коллекция была изменена; операция перечисления может не выполняться. : System.InvalidOperationException: коллекция была изменена; операция перечисления может не выполняться. ». Я не могу воспроизвести это; когда я выполняю ту же обработку тех же данных, моя сборка не генерирует ошибку в этом месте.

Ошибка возникает, когда я вызываю W .Where (). ToArray () ’для объекта с данными: объект класса System.Data.TypedTableBase.

Вот код: ..................

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();
..................

Объект «instance» - это набор данных - мой класс, созданный из 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, Nullable`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 (строковое имя файла, логическое ожидание, логическое удаление файла, строковое имя источника)

Я не могу понять, почему происходит ошибка; и почему это происходит только иногда и больше не происходит с теми же обработанными данными. Ошибка «Коллекция была изменена; операция перечисления может не выполняться ». Само по себе довольно просто; но я не понимаю, почему так происходит в моем коде. Ошибка исключается, если код, подобный этому:

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

Но мой код выше просто хочет перечислить System.Data.TypedTableBase и преобразовать результат в массив.

Есть идеи?

Ответы [ 2 ]

0 голосов
/ 09 апреля 2019

Вы не можете изменять коллекцию, используя 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);
 }
0 голосов
/ 09 апреля 2019

Измените .ToArray () на .ToList ().Между этими двумя понятиями есть семантическая разница, лучше всего иллюстрируемая ответом здесь

Несколько других (хороших) ответов были сосредоточены на микроскопических различиях в производительности, которые могут возникнуть.Этот пост является просто дополнением, чтобы упомянуть семантическое различие, которое существует между IEnumerator, созданным массивом (T []), по сравнению с тем, который возвращается List.Лучше всего это проиллюстрировать на примере:

 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

Это показывает, что IEnumarator, возвращаемый int [], не отслеживает, был ли массив изменен с момента создания перечислителя.Обратите внимание, что я объявил локальную переменную 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).В заключение, перечислитель, созданный списком, отслеживает, меняется ли список во время перечисления, а перечислитель, созданный T [], - нет.Поэтому учитывайте эту разницу при выборе между .ToList () и .ToArray ().Люди часто добавляют один дополнительный .ToArray () или .ToList (), чтобы обойти коллекцию, которая отслеживает, была ли она изменена во время жизни перечислителя.(Если кто-то хочет знать, как List <> отслеживает, была ли изменена коллекция, в этом классе есть закрытое поле _version, которое изменяется при каждом обновлении List <>.)
...