2017-07-25 149 views
1

我重写了System.Windows.Data.CollectionView的行为。有一种方法应该从数据库中清除并重新填充CollectionView.SourceCollection(在我的情况下为ObservableCollection<object>)。抛出异常:WPF CollectionView错误:'集合被修改;枚举操作可能不会执行。'

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#

obse在调试堆栈跟踪中,我开始怀疑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

+0

它可能是由于'result'仍然被填充(在'refreshSourceFunction'方法中),你开始迭代它('foreach')吗? – kennyzx

+0

我想到了,但如果是这种情况,它应该已经在任何尝试2,3或4中通过'.ToList()'解决了。即使没有'.ToList()','result '从未修改过。 @kennyzx –

+0

Dispatcher.BeginInvoke(()=> // {// SourceObservableCollection。新增项目); //},试试这个以及让我们看看 - – Ramankingdom

回答

0

原来问题实际上是在ObservableCollection<T>本身,因为它不是线程安全的。看起来它正在UI线程中被读取,而它仍在被修改,并且问题中描述的与线程有关的解决方法不起作用,因为无论如何CollectionChanged事件正在被提出。使用线程安全版本found here替换ObservableCollection<T>类型解决了该问题。

0

发生此异常。通常当你迭代一个集合时,它会返回一个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 
} 
+0

正如你所看到的,我正在迭代的'IEnumerable'('result')从来没有被修改过。 –

+0

结果是来自SourceCollection的吗?如果是这样的话,你可以在循环中使用result.ToList()。 – Ramankingdom

+0

恐怕不是;它来自数据库。 –

相关问题