C # Модификация IEnumerable загадка, что модифицирует мой IEnumerable? - PullRequest
5 голосов
/ 09 января 2012

Я написал метод расширения для добавления элементов в (EF) EntityCollection.Я получил интересную ошибку, сказав, что моя коллекция IEnumerable ("items") была изменена после первого цикла в foreach.Когда я превращаю элементы в items.ToList () (как в приведенном ниже коде), он работает нормально.

Я полностью понимаю, что выполнение ToList () создаст копию элементов, с которыми затем будет работать foreach..

Что я НЕ понимаю, так это то, что модифицирует IEnumerable, когда я выполняю foreach над ним.

Обновление: каким-то образом кажется, что переменная items такая же, как переменная collection?

Обновление 2: Я думаю, что на коллекцию и сущность может повлиять отслеживание сущности EF, но явсе еще не понимаю, почему

Использование:

ssp.ServiceAreas.ReplaceCollection(model.ServiceAreas);

Вот мой метод расширения:

    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);
    }

Ответы [ 5 ]

4 голосов
/ 09 января 2012
collection.Remove(collection.First(c => c.ID == item.ID)); 

вы удаляете из коллекции, которую вы повторяете.

1 голос
/ 09 января 2012

Я создал следующий пример кода:

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);
        }
    }

Это работает, как и следовало ожидать, ошибок нет. Так что, по сути, ни одна из строк не вызывает проблем.

Что вызовет проблемы, если вы сами вызовете список:

one.AddOrUpdate(one);

Итак, насколько я вижу, вы должны вызывать этот метод расширения с той же коллекцией, что и оба аргумента.

Если да, то Remove или Add будут мутировать коллекцию и вызывать это исключение.

0 голосов
/ 03 февраля 2012
EntityCollection<Customer> customers = new EntityCollection<Customer>();
Customer newCustomer = new Customer() {ID = 0};
customers.Add(newCustomer);
customers.AddOrUpdate(customers);
0 голосов
/ 10 января 2012

Может случиться так, что первый элемент всегда будет новым, и поэтому по умолчанию для идентификатора установлено какое-то начальное значение.Затем он будет добавлен в коллекцию, позволяя EntityFramework сгенерировать новый идентификатор и назначить его первому добавленному элементу.

Тогда может случиться так, что EntityCollection сочтет, что он изменился, поскольку использует идентификатор для сортировкиили сделать что-то еще внутри.И, таким образом, операция foreach (которая, вероятно, использует тот же список) выдает исключение.Вот почему тестовый пример, доказанный Адамом Хоулдсвортом, не дает проблемы!

0 голосов
/ 09 января 2012

Может быть, EntityCollection не нравится, когда кто-то захватывает его элементы?Поэтому, когда вы добавляете в collection, элемент удаляется из items.

Или, может быть, items == collection

...