2017-07-25 69 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

恐怕不是;它來自數據庫。 –

相關問題