2

I do know that changing collection while you are enumeration within it will cause collection was modified exception. But if I get a subcollection from larger one, and while I'm enumerating that subcolletion I remove some item from larger one I still get this error. Invoking ToList on subcollection solves this issue. But why it occurs?

var localCollection = someData.ToList(); // from DB Context
var localGrouped = localCollection.GroupBy(x => x.Id).Select(g => new { Id = g.Key, List = g.Select(x => x.Value) }); or .ToList(); // Here how I solve exception

var groups = new List<List<Int64>>();

while (localGrouped.Any())
{
    var newSelected = new List<Int64>();

    var firstGroup = localGrouped.First();
    newSelected.Add(firstGroup.Id);

    localGrouped.Remove(firstGroup);

    var similiarGroups = localGrouped.Where(x => x.List.Intersect(firstGroup.List).Any()).ToList();
    if (similiarGroups.Any())
    {
        foreach (var similiarGroup in similiarGroups)
        {
        //Changing something here in parent collection causes exception
            newSelected.Add(similiarGroup.Id);
            localGrouped.Remove(similiarGroup);
        }
    }
    groupsOfParcels.Add(newSelected);
}
jwg
  • 5,547
  • 3
  • 43
  • 57
Johnny_D
  • 4,592
  • 3
  • 33
  • 63
  • You are still enumerating the outer collection while inside the subcollection. – jamesSampica Apr 30 '14 at 16:55
  • Not so @Shoe. The outside loop is `while (localGrouped.Any())`, which will iterate `localGrouped` at the start of each iteration, but isn't iterating *over* it. – jwg Apr 30 '14 at 17:15

2 Answers2

8

Where is not a subcollection, it's a filter. The difference goes to the heart of how LINQ works.

You can think of Where as, roughly, saving a reference to the original IEnumerable, along with some code that checks for the condition, and some state which says how far through it has gone. When you do getNext() on the output of Where, the piece of code goes through the original IEnumerable until it finds an element which satisfies the condition, then returns it (or gets to the end of the original IEnumerable, which means that it's also at the end of Where).

This is lazy evaluation - it only looks at as many terms as it needs to at any one time. So the original IEnumerable has to be present and unmodified the whole way along. If you call ToList(), the evaluation will take place straight away - all the elements will be extracted and put into a list before continuing.

The best stuff to read about this is by C# and Linq god Jon Skeet. His posts include reimplementations of all the main Linq functions with detailed discussion of implementation issues, so that you can see exactly how they (probably) work.

Introduction

Part 2 - Where(!)

jwg
  • 5,547
  • 3
  • 43
  • 57
5

GroupBy, Where, Select, and most other LINQ operations are simply grabbing the IEnumerable from the underlying collection and iterating it. That underlying IEnumerable is throwing an exception when it tries to get the next item, because the collection was modified. That exception is thrown up through each LINQ operator because if it can't get the next item from the underlying sequence, it can't do its job.

By using ToList you force the entire sequence to be enumerated before you have modified the collection, rather than allowing the enumerating of the underlying list to be deferred until after that list has been modified.

Servy
  • 202,030
  • 26
  • 332
  • 449
  • any thought on this http://stackoverflow.com/questions/36215695/not-able-to-dispose-datatable-in-foreach-loop? – Neel Apr 07 '16 at 07:21
  • @CoreDeveloper You're modifying a collection while you're iterating it. – Servy Apr 07 '16 at 14:28
  • but I have used .ToList as shown in answer. it can still appear any chance? – Neel Apr 08 '16 at 07:17
  • @CoreDeveloper Of course it can. Calling `ToList` doesn't magically mean that the collection can't be modified while it's being iterated. – Servy Apr 08 '16 at 13:08
  • then what does this mean http://stackoverflow.com/questions/604831/collection-was-modified-enumeration-operation-may-not-execute? – Neel Apr 08 '16 at 13:36
  • @CoreDeveloper It means you can't modify a collection while you're iterating over it. – Servy Apr 08 '16 at 13:39
  • I meant the solution in this question, it says when we convert it to .ToList during looping then it should not throw collection modified error – Neel Apr 08 '16 at 14:21
  • @CoreDeveloper In some circumstances, it may, depending on what the collection is, and what is modifying it. In other cases, it won't change a thing. That's why it's important to understand the core concepts, rather than using cargo cult solutions. You need to not modify the collection while you're iterating it, which means figuring out what is modifying the collection, and when, with respect to its iteration, rather than blindly copy-pasting solutions that you don't understand. – Servy Apr 08 '16 at 14:27