实体框架内的不一致错误“,集合已修改;枚举操作可能无法执行。 [英] Inconsitency error inside Entity Framework , "Collection was modified; enumeration operation may not execute."

查看:211
本文介绍了实体框架内的不一致错误“,集合已修改;枚举操作可能无法执行。的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的asp.net mvc Web应用程序中有以下操作方法: -

  [HttpPost,ActionName(Delete )] 
[ValidateAntiForgeryToken]
public ActionResult DeleteConfirmed(int id)
{
Dept dept = db.Depts.Include(a => a.Emps).Single a => a.DeptID == id);

var emps = dept.Emps;
foreach(var c in emps)
{
dept.Emps.Remove(c);

}
db.Depts.Remove(dept);
db.SaveChanges();
return RedirectToAction(Index);
}

现在,这将引发以下异常,在 foreach 代码: -



系统中出现了System.InvalidOperationException类型的异常。 Core.dll但未在用户代码中处理



其他信息:集合已修改;枚举操作
可能无法执行。


但是,如果我只是将.ToList()添加到emps,如下所示:

  var emps = dept.Emps.ToList(); 
foreach(var c in emps)
{

错误将消失。但我的问题是为什么我需要添加一个.ToList()?因为在我的情况下,dept变量应该具有来自数据库的相关的emps记录,因为我添加了一个.Include(),所以强制错误消失的.Tolist()不应该有任何影响。
有人可以请教这个吗?
谢谢

解决方案

我们假设你有一堆你要算的便士, / p>

1,2,3,4 ...你有一半的时间到了,然后有人来了,把他们全部重新洗牌,你输了。



正是当您进入foreach时调用Remove时,会发生什么。由于您在枚举中间时删除了一个项目,所以foreach丢失轨道。



当添加一个.ToList()时,它将创建在内存中的List对象中的集合的副本,您正在枚举..所以当您在原始 dept.Emps 上调用remove时,没有冲突,因为您不是枚举该集合。你正在枚举副本。您的副本仍然具有您删除的项目,但DataContext现在不存在。



在该示例中,您有两个相同的便士(原件和副本)你开始计算一套...有人来了,混乱了第二套,但是对你来说并不重要,因为你的设置还是很好,你可以继续下去。



这与使用Include无关。你使用.ToList()来使用.ToList()来执行一个查询来创建一个数据的副本。



编辑:



简单的事实是,您在枚举时不能修改集合。一旦你修改它,通过调用删除或插入或其他任何东西,枚举器将变得无效,并继续您的foreach引发异常。



调用ToList()创建一个副本的原始集合,它允许您枚举副本,而您修改原始。因此,您的副本的枚举者不会失效。



这不是DataContext,它与任何类型的集合都是一样的。列表,DbSets,字典,哈希表,无论如果你枚举的东西,你不能修改,而你是枚举它,而不使枚举器无效。



你将有完全相同的这个代码的问题:

 列表< int> myList = new List< int> {1,2,3}; 

foreach(int x in myList)
{
if(x == 2)
myList.Remove(x);
}

你必须这样做,因为.ToList()创建一个原始列表:

 列表< int> myList = new List< int> {1,2,3}; 

foreach(int x in myList.ToList())//即使myList已经是一个列表
{
if(x == 2)
myList。删除(X);
}

这里的意思是这个问题与数据库无关,或者包括,或者单()或其他任何东西...在枚举它时,修改集合是一个严格的问题。而已。就是这样,没有什么了。


I have the following action method inside my asp.net mvc web application:-

[HttpPost, ActionName("Delete")]
        [ValidateAntiForgeryToken]
        public ActionResult DeleteConfirmed(int id)
        {
            Dept dept = db.Depts.Include(a => a.Emps).Single(a=>a.DeptID == id);

            var emps = dept.Emps;
            foreach (var c in emps)
            {
                dept.Emps.Remove(c);

            }
          db.Depts.Remove(dept);
            db.SaveChanges();
            return RedirectToAction("Index");
        } 

now this will raise the following exception , inside the foreach code :-

An exception of type 'System.InvalidOperationException' occurred in System.Core.dll but was not handled in user code

Additional information: Collection was modified; enumeration operation may not execute.

but if i simply add a .ToList() to the emps as follow:-

var emps = dept.Emps.ToList();
                foreach (var c in emps)
                {

the error will disappear. but my question is why i need to add a .ToList() ? since in my case the dept variable should have the related emps records from the database , because i have added a .Include(), so the .Tolist() which will force the error to disappear should not have any effect.. ? Can anyone advice on this please? Thanks

解决方案

Let's say you have a bunch of pennies you're counting, one by one...

1, 2, 3, 4... and you get about halfway through, and then someone comes along and reshuffles them all up on you.. and you lose count.

That's exactly what happens when you call Remove while you are in a foreach. The foreach "loses track" of the collection because you removed an item while it was in the middle of enumerating it.

When you add a .ToList(), it creates a copy of the collection in a List object in memory, which you are enumerating.. so when you call remove on the original dept.Emps, there is no conflict, since you are not enumerating that collection. You're enumerating the copy. Your copy still has the item you removed, but the DataContext now does not.

In the example, you have two identical sets of pennies (the original and a copy), you start counting one set... and someone comes along and messes up the second set, but it doesn't matter to you because your set is still just fine, you can keep going.

This has nothing to do with using Include. You are confusing using .ToList() to execute a query with using .ToList() to make a copy of the data.

EDIT:

The simple fact is that you cannot modify a collection while you are enumerating it. As soon as you modify it, by calling Remove or Insert or anything else, the enumerator becomes invalid and continuing your foreach throws an exception.

Calling ToList() creates a copy of the original collection, which allows you to enumerate the copy, while you modify the original. Therefore, the enumerator of your copy does not become invalidated.

It doesn't matter that this is a DataContext, it works the same with any kind of collection. Lists, DbSets, Dictionaries, HashSets, whatever.. if you enumerate something, you cannot modify while you are enumerating it without invalidating the enumerator.

You will have the exact same problem with this code:

List<int> myList = new List<int>{1, 2, 3};

foreach(int x in myList)
{
    if (x == 2)
        myList.Remove(x);
}

You will have to do this, since .ToList() creates a copy of the original List:

List<int> myList = new List<int>{1, 2, 3};

foreach(int x in myList.ToList()) // Even though myList is already a list
{
    if (x == 2)
        myList.Remove(x);
}

The point here is that this problem has nothing to do with databases, or includes, or Single() or anything else... it's strictly a problem with modify a collection while you are enumerating it. That's it. That's all it is, and nothing more.

这篇关于实体框架内的不一致错误“,集合已修改;枚举操作可能无法执行。的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆