为什么我的ObservableCollection扩展会导致运行时异常?

时间:2013-11-04 20:35:25

标签: c# .net multithreading

使用.NET 4.0,我有一个宿主应用程序,它解析csv文件,对每行数据进行说明,并通过WCF回调将其作为类型返回给我的客户端。这部分工作正常。我开始遇到麻烦的地方是当我尝试将该类型或类型集合添加到MainWindow中的ObservableCollection时。

所以这......

 public class MyServiceCallback : IMyServiceCallback
    {
        //List<Location.LocationData> lastData = new List<Location.LocationData>();
        //Dictionary<string, Location.LocationData> lastData = new Dictionary<string,Location.LocationData>();
        //Network exposed Callback method which recieves Host/Client common data type
        //Note: Ought to be an interface and not a class type, but not needed for a small project
        public void OnCallback(Location.LocationData[] t)
        {
            //if(t.Where(x=>x.Frequency == lastData[
            //foreach (Location.LocationData d in t)
            //{
            //    lastData.Add(d.Frequency, d);
            //}
            //Call static method on MainWindow to pass the collection of LocationData to UI bound LocationList
            if(!(t.Length == 0))
            Client.MainWindow.SetLocationList(t.ToList());            
        }
    }

从WCF主机调用,而SetLocation(t.ToList())调用此...

public partial class MainWindow : Window
    {

private static MTObservableCollection<Location.LocationData> locationList = new MTObservableCollection<Location.LocationData>();

public static MTObservableCollection<Location.LocationData> LocationList
{
     get { return locationList; }
     set { locationList = value; }
} 

public static void SetLocationList(List<Location.LocationData> hostPushedLocationData)
        {
            //Clear previous location data
            LocationList.Clear();

            System.Threading.Thread.SpinWait(1);

            //Add the pushed data to the bound collection to be displayed
            hostPushedLocationData.ForEach(data => { LocationList.Add(data); System.Threading.Thread.SpinWait(1); });

        }
}

如果我使用普通的ObservableCollection,这根本不起作用,因为我无法从WCF线程更新集合。

如果我用这个扩展ObservableCollection ......

public class MTObservableCollection<T> : ObservableCollection<T>
    {
        public override event NotifyCollectionChangedEventHandler CollectionChanged;
        protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            NotifyCollectionChangedEventHandler CollectionChanged = this.CollectionChanged;
            if (CollectionChanged != null)
                foreach (NotifyCollectionChangedEventHandler nh in CollectionChanged.GetInvocationList())
                {
                    DispatcherObject dispObj = nh.Target as DispatcherObject;
                    if (dispObj != null)
                    {
                        Dispatcher dispatcher = dispObj.Dispatcher;
                        if (dispatcher != null && !dispatcher.CheckAccess())
                        {
                            dispatcher.BeginInvoke(
                                (Action)(() => nh.Invoke(this,
                                    new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset))),
                                DispatcherPriority.DataBind);
                            continue;
                        }
                    }
                    nh.Invoke(this, e);
                }
        }

    }

我在这里找到:Where do I get a thread-safe CollectionView?

我间歇性地收到了这个错误:

我使用此版本时遇到异常,但在使用Jonathan提供的版本时却没有。有没有人有想法为什么会这样?这是我的InnerException:抛出此异常,因为名为'OrdersGrid'的控件'System.Windows.Controls.DataGrid Items.Count:3'的生成器收到了与Item的当前状态不一致的CollectionChanged事件序列采集。检测到以下差异:累计计数2与实际计数3不同。[累计计数为(上次重置计数+ #Adds - 自上次重置后#Removes)

在链接帖子中提及。

我也经常遇到这个错误:索引超出了范围。

如果有人可以帮助或指出我正确的方向,我需要对此集合扩展做什么来解决这个问题,我将不胜感激。此外,如果我使用.NET 4.5而不是4.0来解决这个问题吗?

非常感谢!

1 个答案:

答案 0 :(得分:0)

您将通过Dispatcher.BeginInvoke()解雇您的收藏更改事件。在您安排活动的时间和事件实际触发的时间之间,该集合可能会多次更改。基于异常消息,听起来这正是发生的事情。

不安排稍后提出的更改事件;它们意味着立即被提升。