Home > front end >  Unknown behavior on IEnumerable and LINQ
Unknown behavior on IEnumerable and LINQ

Time:12-29

When I convert ConcurrentBag to array and add condition ".Where(x => !x.IsDataLoaded)", I have unknown behavior on this collection.

 var all = rollings.ToArray();
 var copy = all
     .OrderBy(x => x.Id)
     .Where(x => !x.IsDataLoaded);

Next, I enumerate collection by foreach. Every iteration I logout debug info - copy.Count(), and this count decreased every iteration loop BUT I have NOT see "System.InvalidOperationException: Collection was modified"

foreach (var rolling in copy)
{
    ...
    rolling.IsDataLoaded = true;
    Log($"copy count: {copy.Count}");
}

So next I try copy.Max() but collection is empty and it throws exception:

DateTime beforeDatetime = DateTime.MinValue;
try
{
    beforeDatetime = copy.Max(x => x.To);
}
catch (Exception ex)
{
    Log(ex.ToString());
    Log($"copy count: {copy.Count}");
}

Does the LINQ work by lazy loading? This will turn my life around.

Code executing

CodePudding user response:

LINQ is deferred execution. So it will not filter the data until you do something to force it to apply the filter (such as .ToList(), .ToArray() or running a foreach over it.)

So your copy is just setting up a filter on all, and that filter won't be applied until the foreach (var rolling in copy). And since your are mutating the data in the underlying objects that LINQ is processing while it is processing it, the conditions for the filter are changing as it is trying to filter the data, so it will produce unexpected results.

You put that it works if you put a .ToList() at the end of setting up copy, which is because it is now at that point that you force the LINQ filter to be applied. i.e. before you start looping over it.

Your try block fails because you are re-applying the filter. The IDE (if you're using Visual Studio) usually puts a wavey underling (or that could be ReSharper doing that) under the second time you use copy to alert you to the fact that you're re-applying the filter and forcing the LINQ enumerable to be reevaluated as .Max() is another one of those methods that force the LINQ expression to be run. If you put the .ToList() when you create the copy then the filter is evaluated once at that point, your copy is a list whose contents don't change when you mutate the data in that list.

  • Related