Question

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);
}
Was it helpful?

Solution

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(!)

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top