Я получаю сообщение "Коллекция была изменена; операция перечисления может не выполняться". во время foreach - PullRequest
1 голос
/ 28 марта 2012

Мне нужно подтверждение моего подхода для этого, я использую EF и ASP.NET MVC, и я пытаюсь удалить объекты на основе выбора пользователя (то есть на основе того, что они проверяли / не проверяли).

Для этого я смотрю на идентификаторы, которые передаются из формы из флажков, совпадают с тем, что у меня есть в базе данных, а затем сначала добавляются все новые, а затем удаляются все не соответствующие,

Ниже приведен код, который у меня изначально был:

        [HttpPost]
    public ActionResult Edit(int id, FormCollection collection, VMinstanceRole vmodel)
    {
        try
        {
            var instancerole = db.instanceRoles.Find(id);

            if (ModelState.IsValid)
            {
                UpdateModel<instanceRole>(instancerole, "instanceRole");
                var keys = instancerole.rights.Select( c => c.Id);

                foreach (var pid in vmodel.selectedId.Except(keys))
                {
                    var right = new right { Id = pid };
                    db.rights.Attach(right);
                    instancerole.rights.Add(right);
                }

                foreach (var pid in keys.Except(vmodel.selectedId))
                {
                    var right = instancerole.rights.Where(c => c.Id == pid).Single();
                    instancerole.rights.Remove(right);
                }


                db.SaveChanges();
            }

            // TODO: Add update logic here

            return RedirectToAction("Index");
        }
        catch (InvalidCastException e) 
        {
            return View();
        }
    }

Однако была представлена ​​следующая ошибка: «Сбор был изменен; операция перечисления может не выполняться».

Поэтому, чтобы попытаться решить эту проблему, я решил сохранить отдельный список и затем удалить его на основе списка, чтобы устранить ошибку:

        [HttpPost]
    public ActionResult Edit(int id, FormCollection collection, VMinstanceRole vmodel)
    {
        try
        {
            var instancerole = db.instanceRoles.Find(id);
            List<right> removeList = new List<right>();
            if (ModelState.IsValid)
            {
                UpdateModel<instanceRole>(instancerole, "instanceRole");
                var keys = instancerole.rights.Select( c => c.Id);

                foreach (var pid in vmodel.selectedId.Except(keys))
                {
                    var right = new right { Id = pid };
                    db.rights.Attach(right);
                    instancerole.rights.Add(right);
                }

                foreach (var pid in keys.Except(vmodel.selectedId))
                {
                    var right = instancerole.rights.Where(c => c.Id == pid).Single();
                    removeList.Add(right);
                }

                foreach (var right in removeList)
                {
                    instancerole.rights.Remove(right);
                }
                db.SaveChanges();
            }

            // TODO: Add update logic here

            return RedirectToAction("Index");
        }
        catch (InvalidCastException e) 
        {
            return View();
        }
    }

Кажется, это работает, но я 'Я не уверен, правильно ли я поступил.Главным образом, потому что я делаю еще один цикл.Есть ли лучший способ приблизиться к этому или это достаточно хорошо?

Ответы [ 3 ]

4 голосов
/ 28 марта 2012

Вы нашли одно стандартное решение. Другое решение, которое работает, заключается в том, чтобы вызывать ToList для операции LINQ, которая создает ваш объект keys: при этом отключается ключ от коллекции instanceroles, что допускает произвольные независимые изменения исходной коллекции.

0 голосов
/ 28 марта 2012

Попробуйте это:

foreach (var pid in keys.Except(vmodel.selectedId).ToList())
{
    var right = instancerole.rights.Where(c => c.Id == pid).Single();
    instancerole.rights.Remove(right);
}

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

0 голосов
/ 28 марта 2012

Причина неспособности редактировать коллекцию при перечислении с foreach достаточно хорошо документирована здесь одна (просто проверьте «связанные» ссылки на стороне), и, зная, что вы не можете этого сделать, вы могли бы используйте простой цикл for и изменяйте индекс при удалении элемента - это позволяет поддерживать один цикл.

for (int i = 0; i < max; i++) {
  //if removing an item 
  //manipulate the index as desired...
  i--;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...