我收到“收集已修改;枚举操作可能无法执行”。在foreach期间

时间:2012-03-28 10:55:39

标签: c# asp.net asp.net-mvc-3 entity-framework-4.1

我需要确认我的方法,我正在使用EF和ASP.NET MVC,我试图根据用户选择删除实体(即基于他们检查/取消选中的内容)。

要做到这一点,我正在查看从复选框中的表单传递的ID,匹配我在数据库中的内容,然后首先添加任何新的,然后删除任何不匹配的。

以下是我最初的代码:

        [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 个答案:

答案 0 :(得分:4)

您找到了一个标准解决方案。另一个有效的解决方案是在生成密钥对象的LINQ操作上调用ToList:这样做断开密钥与instanceroles集合,允许对原始集合进行任意独立修改。

答案 1 :(得分:0)

在使用foreach进行枚举时无法编辑集合的原因已足够详细记录(仅检查“相关”链接到侧面),并且知道您不能这样做,您可以使用简单的for循环并在删除项目时修改索引 - 这样可以保持一个循环。

for (int i = 0; i < max; i++) {
  //if removing an item 
  //manipulate the index as desired...
  i--;
}

答案 2 :(得分:0)

试试这个:

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

在删除第一个项目时,您在foreach循环中枚举的枚举器将被处理掉。