Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C# Modification of IEnumerable mystery, what is modifying my IEnumerable?

I've written an extension method to add items to an (EF) EntityCollection. I got an interesting error, saying that my collection of IEnumerable ("items") had been modified, after the first loop in the foreach. When I turn items into items.ToList() (like in the code below), it works fine.

I completely understand that doing ToList() will produce a copy of the items on which the foreach will then operate.

What I do NOT understand is what is modifying the IEnumerable when I'm doing a foreach over it.

Update: Somehow, it seems the items variable is the same as the collections variable?

Update 2: I think collection and entity may be affected by EF's entity tracking, but I still fail to understand why

Usage:

ssp.ServiceAreas.ReplaceCollection(model.ServiceAreas);

Here's my extension method:

    public static void AddOrUpdate<TEntity>(this EntityCollection<TEntity> collection, IEnumerable<TEntity> items)
        where TEntity : EntityObject, IProjectIdentity<int>, new()
    {
        foreach (var item in items.ToList())
            collection.AddOrUpdate(item);
    }

    public static void AddOrUpdate<TEntity>(this EntityCollection<TEntity> collection, TEntity item)
        where TEntity : EntityObject, IProjectIdentity<int>, new()
    {
        if (item.ID > 0 && collection.Any(c => c.ID == item.ID))
            collection.Remove(collection.First(c => c.ID == item.ID));
        // For now, the Remove NEVER gets hit

        collection.Add(item);
    }
like image 458
sebastiaan Avatar asked Jan 09 '12 14:01

sebastiaan


2 Answers

collection.Remove(collection.First(c => c.ID == item.ID)); 

you are removing in the collection you are iterating.

like image 146
Per Kastman Avatar answered Sep 30 '22 02:09

Per Kastman


I created the following sample code:

internal class Program
    {
        private static void Main(string[] args)
        {
            var one = new List<string> {"Adam", "John"};

            var two = new List<string> {"Adam", "Houldsworth"};

            one.AddOrUpdate(two);

            Console.Read();
        }
    }

    static class Extensions
    {
        public static void AddOrUpdate(this IList<string> collection, IEnumerable<string> items)
        {
            foreach (var item in items.ToList())
                collection.AddOrUpdate2(item);
        }

        public static void AddOrUpdate2(this IList<string> collection, string item)
        {
            if (collection.Any(c => c == item))
                collection.Remove(collection.First(c => c == item));

            collection.Add(item);
        }
    }

This works as you would expect, there are no errors. So in essence, none of the lines are causing issues.

What will cause issues is if you call the list on itself:

one.AddOrUpdate(one);

So from what I can see, you must be calling this extension method with the same collection as both arguments.

If you are, then both Remove or Add will mutate the collection and cause this exception.

like image 37
Adam Houldsworth Avatar answered Sep 30 '22 02:09

Adam Houldsworth