WPF CollectionView Error: 'Collection was modified; enumeration operation may not execute.'
WPF CollectionView Error: 'Collection was modified; enumeration operation may not execute.'
我正在覆盖 System.Windows.Data.CollectionView
的行为。有一种方法应该从数据库中清除并重新填充 CollectionView.SourceCollection
(在我的例子中是 ObservableCollection<object>
)。抛出异常:
Exception thrown: 'System.InvalidOperationException' in mscorlib.dll
Additional information: Collection was modified; enumeration operation
may not execute.
恰好在第二次命中此行时抛出 SourceObservableCollection.Add(item);
。
(注释行描述了我解决问题的失败尝试):
//...
public ObservableCollection<object> SourceObservableCollection { get { return (ObservableCollection<object>)SourceCollection; } }
//<Part of Attempt7>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
base.OnCollectionChanged(args);
isCollectionChanging = false;
}
private bool isCollectionChanging = false;
//</Part of Attempt7>
//<Part of Attempt9>
private static readonly object _lock = new object();
//</Part of Attempt9>
//<*async syntax is part of Attempt10*/>
public async void RefreshSource()
{
SourceObservableCollection.Clear();
// refreshSourceFunction retrieves data from Database
IEnumerable result = refreshSourceFunction(/*parameters*/);
////Attempt1:
foreach (object item in result)
{
SourceObservableCollection.Add(item);
}
////Attempt2:
//foreach (object item in result.OfType<object>().ToList())
//{
// SourceObservableCollection.Add(item);
//}
////Attempt3:
//List<object> lstResult = result.OfType<object>().ToList();
//foreach (object item in lstResult)
// SourceObservableCollection.Add(item);
////Attempt4:
//List<object> lstResult2 = result.OfType<object>().ToList();
//for (int x = 0; x < lstResult2.Count; x++)
//{
// SourceObservableCollection.Add(lstResult2[x]);
//}
////Attempt5:
//IEnumerator enumerator = result.GetEnumerator();
//while (enumerator.MoveNext())
//{
// SourceObservableCollection.Add(enumerator.Current);
//}
////Attempt6:
//IEnumerator enumerator2 = result.GetEnumerator();
//while (enumerator2.MoveNext())
//{
// Dispatcher.Invoke(() =>
// {
// SourceObservableCollection.Add(enumerator2.Current);
// });
//}
////Attempt7:
//foreach (object item in result)
//{
// isCollectionChanging = true;
// Dispatcher.Invoke(() =>
// {
// SourceObservableCollection.Add(item);
// }, System.Windows.Threading.DispatcherPriority.Background);
// while (isCollectionChanging) ;
//}
////Attempt8:
//foreach (object item in result)
//{
// SourceObservableCollection.Add(item);
// Refresh();
//}
////Attempt9:
//foreach (object item in result)
//{
// lock (_lock)
// {
// SourceObservableCollection.Add(item);
// }
//}
////Attempt10:
//await Dispatcher.InvokeAsync(() => SourceObservableCollection.Clear());
//IEnumerable result2 = await Task.Run(() => refreshSourceFunction(/*parameters*/));
//foreach (object item in result2)
//{
// await Dispatcher.InvokeAsync(() => SourceObservableCollection.Add(item));
//}
}
//...
异常 StackTrace
只有这个:
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
但是,调试调用堆栈是:
mscorlib.dll!System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource
resource) Unknown
mscorlib.dll!System.Collections.Generic.List.Enumerator.MoveNextRare() Unknown
mscorlib.dll!System.Collections.Generic.List.Enumerator.MoveNext() Unknown
PresentationFramework.dll!MS.Internal.Data.IndexedEnumerable.EnsureEnumerator() Unknown
PresentationFramework.dll!MS.Internal.Data.IndexedEnumerable.EnsureCacheCurrent() Unknown
PresentationFramework.dll!MS.Internal.Data.IndexedEnumerable.Count.get() Unknown
PresentationFramework.dll!System.Windows.Data.CollectionView.Count.get() Unknown
PresentationFramework.dll!System.Windows.Data.CollectionView.AdjustCurrencyForAdd(int
index) Unknown
PresentationFramework.dll!System.Windows.Data.CollectionView.ProcessCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs
args) Unknown
PresentationFramework.dll!System.Windows.Data.CollectionView.OnCollectionChanged(object
sender,
System.Collections.Specialized.NotifyCollectionChangedEventArgs
args) Unknown
System.dll!System.Collections.ObjectModel.ObservableCollection.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs
e) Unknown
System.dll!System.Collections.ObjectModel.ObservableCollection.InsertItem(int
index, System.__Canon item) Unknown
mscorlib.dll!System.Collections.ObjectModel.Collection.Add(object
item) Unknown
MyDll.dll!MyDll.MyNamespace.MyOverriddenCollectionView.RefreshSource() Line 105 C#
观察调试堆栈跟踪后,我开始怀疑 MS.Internal.Data.IndexedEnumerable
方法,尤其是在 observing it in ReferenceSource 之后;如您所见,多线程使用是不安全的:
/// <summary>
/// for a collection implementing IEnumerable this offers
/// optimistic indexer, i.e. this[int index] { get; }
/// and cached Count/IsEmpty properties and IndexOf method,
/// assuming that after an initial request to read item[N],
/// the following indices will be a sequence for index N+1, N+2 etc.
/// </summary>
/// <remarks>
/// This class is NOT safe for multi-threaded use.
/// if the source collection implements IList or ICollection, the corresponding
/// properties/methods will be used instead of the cached versions
/// </remarks>
internal class IndexedEnumerable : IEnumerable, IWeakEventListener
{
//...
但是,我仍然不知道如何解决这个问题,甚至不知道到底出了什么问题。任何帮助将不胜感激。
当前 .Net Framework 版本:4.5
当您迭代一个集合并尝试修改它时,会发生此异常。通常,当您遍历一个集合时,它 returns 一个 IEnumerable ,您可以将其假定为一个指针,依次向前移动。假设你改变了集合然后迭代器变得无效并且框架抛出无效操作异常
例如(伪代码)
Foreach(var item in collection)
{
modify collection here; (Do some add , remove or clear operation)
}
上面的代码肯定会抛出异常
为了处理这种你必须遍历集合并想做一些修改操作的场景使用索引
例如(伪代码)
for(int i=0; i< collection.count(); i++)
{
// do anything here
}
事实证明问题实际上出在 ObservableCollection<T>
本身,因为它不是线程安全的。它似乎在 UI 线程中被读取,同时它仍在被修改,并且问题中描述的与线程相关的解决方法不起作用,因为 CollectionChanged
事件正在无论如何提出。用线程安全版本 found here 替换类型 ObservableCollection<T>
解决了问题。
我正在覆盖 System.Windows.Data.CollectionView
的行为。有一种方法应该从数据库中清除并重新填充 CollectionView.SourceCollection
(在我的例子中是 ObservableCollection<object>
)。抛出异常:
Exception thrown: 'System.InvalidOperationException' in mscorlib.dll
Additional information: Collection was modified; enumeration operation may not execute.
恰好在第二次命中此行时抛出 SourceObservableCollection.Add(item);
。
(注释行描述了我解决问题的失败尝试):
//...
public ObservableCollection<object> SourceObservableCollection { get { return (ObservableCollection<object>)SourceCollection; } }
//<Part of Attempt7>
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs args)
{
base.OnCollectionChanged(args);
isCollectionChanging = false;
}
private bool isCollectionChanging = false;
//</Part of Attempt7>
//<Part of Attempt9>
private static readonly object _lock = new object();
//</Part of Attempt9>
//<*async syntax is part of Attempt10*/>
public async void RefreshSource()
{
SourceObservableCollection.Clear();
// refreshSourceFunction retrieves data from Database
IEnumerable result = refreshSourceFunction(/*parameters*/);
////Attempt1:
foreach (object item in result)
{
SourceObservableCollection.Add(item);
}
////Attempt2:
//foreach (object item in result.OfType<object>().ToList())
//{
// SourceObservableCollection.Add(item);
//}
////Attempt3:
//List<object> lstResult = result.OfType<object>().ToList();
//foreach (object item in lstResult)
// SourceObservableCollection.Add(item);
////Attempt4:
//List<object> lstResult2 = result.OfType<object>().ToList();
//for (int x = 0; x < lstResult2.Count; x++)
//{
// SourceObservableCollection.Add(lstResult2[x]);
//}
////Attempt5:
//IEnumerator enumerator = result.GetEnumerator();
//while (enumerator.MoveNext())
//{
// SourceObservableCollection.Add(enumerator.Current);
//}
////Attempt6:
//IEnumerator enumerator2 = result.GetEnumerator();
//while (enumerator2.MoveNext())
//{
// Dispatcher.Invoke(() =>
// {
// SourceObservableCollection.Add(enumerator2.Current);
// });
//}
////Attempt7:
//foreach (object item in result)
//{
// isCollectionChanging = true;
// Dispatcher.Invoke(() =>
// {
// SourceObservableCollection.Add(item);
// }, System.Windows.Threading.DispatcherPriority.Background);
// while (isCollectionChanging) ;
//}
////Attempt8:
//foreach (object item in result)
//{
// SourceObservableCollection.Add(item);
// Refresh();
//}
////Attempt9:
//foreach (object item in result)
//{
// lock (_lock)
// {
// SourceObservableCollection.Add(item);
// }
//}
////Attempt10:
//await Dispatcher.InvokeAsync(() => SourceObservableCollection.Clear());
//IEnumerable result2 = await Task.Run(() => refreshSourceFunction(/*parameters*/));
//foreach (object item in result2)
//{
// await Dispatcher.InvokeAsync(() => SourceObservableCollection.Add(item));
//}
}
//...
异常 StackTrace
只有这个:
at System.ThrowHelper.ThrowInvalidOperationException(ExceptionResource resource)
但是,调试调用堆栈是:
mscorlib.dll!System.ThrowHelper.ThrowInvalidOperationException(System.ExceptionResource resource) Unknown
mscorlib.dll!System.Collections.Generic.List.Enumerator.MoveNextRare() Unknown
mscorlib.dll!System.Collections.Generic.List.Enumerator.MoveNext() Unknown
PresentationFramework.dll!MS.Internal.Data.IndexedEnumerable.EnsureEnumerator() Unknown
PresentationFramework.dll!MS.Internal.Data.IndexedEnumerable.EnsureCacheCurrent() Unknown
PresentationFramework.dll!MS.Internal.Data.IndexedEnumerable.Count.get() Unknown
PresentationFramework.dll!System.Windows.Data.CollectionView.Count.get() Unknown
PresentationFramework.dll!System.Windows.Data.CollectionView.AdjustCurrencyForAdd(int index) Unknown
PresentationFramework.dll!System.Windows.Data.CollectionView.ProcessCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs args) Unknown
PresentationFramework.dll!System.Windows.Data.CollectionView.OnCollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs args) Unknown
System.dll!System.Collections.ObjectModel.ObservableCollection.OnCollectionChanged(System.Collections.Specialized.NotifyCollectionChangedEventArgs e) Unknown
System.dll!System.Collections.ObjectModel.ObservableCollection.InsertItem(int index, System.__Canon item) Unknown
mscorlib.dll!System.Collections.ObjectModel.Collection.Add(object item) Unknown
MyDll.dll!MyDll.MyNamespace.MyOverriddenCollectionView.RefreshSource() Line 105 C#
观察调试堆栈跟踪后,我开始怀疑 MS.Internal.Data.IndexedEnumerable
方法,尤其是在 observing it in ReferenceSource 之后;如您所见,多线程使用是不安全的:
/// <summary>
/// for a collection implementing IEnumerable this offers
/// optimistic indexer, i.e. this[int index] { get; }
/// and cached Count/IsEmpty properties and IndexOf method,
/// assuming that after an initial request to read item[N],
/// the following indices will be a sequence for index N+1, N+2 etc.
/// </summary>
/// <remarks>
/// This class is NOT safe for multi-threaded use.
/// if the source collection implements IList or ICollection, the corresponding
/// properties/methods will be used instead of the cached versions
/// </remarks>
internal class IndexedEnumerable : IEnumerable, IWeakEventListener
{
//...
但是,我仍然不知道如何解决这个问题,甚至不知道到底出了什么问题。任何帮助将不胜感激。
当前 .Net Framework 版本:4.5
当您迭代一个集合并尝试修改它时,会发生此异常。通常,当您遍历一个集合时,它 returns 一个 IEnumerable ,您可以将其假定为一个指针,依次向前移动。假设你改变了集合然后迭代器变得无效并且框架抛出无效操作异常
例如(伪代码)
Foreach(var item in collection)
{
modify collection here; (Do some add , remove or clear operation)
}
上面的代码肯定会抛出异常
为了处理这种你必须遍历集合并想做一些修改操作的场景使用索引
例如(伪代码)
for(int i=0; i< collection.count(); i++)
{
// do anything here
}
事实证明问题实际上出在 ObservableCollection<T>
本身,因为它不是线程安全的。它似乎在 UI 线程中被读取,同时它仍在被修改,并且问题中描述的与线程相关的解决方法不起作用,因为 CollectionChanged
事件正在无论如何提出。用线程安全版本 found here 替换类型 ObservableCollection<T>
解决了问题。