c# - 将项目添加到 ObservableCollection 时的线程问题

标签 c# multithreading wcf observablecollection wcf-data-services

我正在 WCF 数据服务异步查询回调方法中更新 WPF ViewModel 的 ObservableCollection:

ObservableCollection<Ent2> mymodcoll = new ObservableCollection<Ent2>();
 ...
query.BeginExecute(OnMyQueryComplete, query);
 ...
private void OnMyQueryComplete(IAsyncResult result)
    {
        ...
        var repcoll = query.EndExecute(result);

        if (mymodcoll.Any())
        {
            foreach (Ent c in repcoll)
            {
                var myItem = mymodcoll.Where(p => p.EntID == c.EntID).FirstOrDefault();
                if (myItem != null) 
                {
                    myItem.DateAndTime = c.DateAndTime; // here no problems
                    myItem.Description = c.Description;
                     ...
                }
                else
                {
                    mymodcoll.Add(new Ent2 //here I get a runtime error
                    {
                        EntID = c.EntID,
                        Description = c.Description,
                        DateAndTime = c.DateAndTime,
                        ...
                    });
                }
            }
        }
        else
        {
            foreach (Ent c in repcoll)
            {
                mymodcoll.Add(new Ent2 //here, on initial filling, there's no error
                {
                    EntID = c.EntID,
                    Description = c.Description,
                    DateAndTime = c.DateAndTime,
                    ...
                });
            }
        }
    }  

问题是,当查询结果集合包含目标集合中不存在的项目并且我需要添加该项目时,我会收到运行时错误:调用线程无法访问该对象因为另一个线程拥有它。(我通过评论指出了这行代码)

然而,如果目标集合为空(在初始填充时),所有项目都已添加,没有任何问题。 (这部分代码我也通过评论指出)。当一个项目只需要更新它的一些字段时,也没有问题,项目更新正常。

我该如何解决这个问题?

最佳答案

第一种情况:在这里您修改了集合中的对象,而不是集合本身 - 因此是 CollectionChanged事件未被触发。

第二种情况:在这里,您从另一个线程 CollectionChanged 向集合中添加一个新元素。事件被触发。由于数据绑定(bind),该事件需要在UI线程中执行。

这个问题我已经遇到好几次了,解决方案不是很好(如果有人有更好的解决方案,请告诉我!)。您必须从 ObservableCollection<T> 派生并将其委托(delegate)给 BeginInvokeInvoke GUI 线程的调度程序上的方法。

例子:

public class SmartObservableCollection<T> : ObservableCollection<T>
{
    [DebuggerStepThrough]
    public SmartObservableCollection(Action<Action> dispatchingAction = null)
        : base()
    {
        iSuspendCollectionChangeNotification = false;
        if (dispatchingAction != null)
            iDispatchingAction = dispatchingAction;
        else
            iDispatchingAction = a => a();
    }

    private bool iSuspendCollectionChangeNotification;
    private Action<Action> iDispatchingAction;

    [DebuggerStepThrough]
    protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
    {
        if (!iSuspendCollectionChangeNotification)
        {
            using (IDisposable disposeable = this.BlockReentrancy())
            {
                iDispatchingAction(() =>
                {
                    base.OnCollectionChanged(e);
                });
            }
        }
    }
    [DebuggerStepThrough]
    public void SuspendCollectionChangeNotification()
    {
        iSuspendCollectionChangeNotification = true;
    }
    [DebuggerStepThrough]
    public void ResumeCollectionChangeNotification()
    {
        iSuspendCollectionChangeNotification = false;
    }


    [DebuggerStepThrough]
    public void AddRange(IEnumerable<T> items)
    {
        this.SuspendCollectionChangeNotification();
        try
        {
            foreach (var i in items)
            {
                base.InsertItem(base.Count, i);
            }
        }
        finally
        {
            this.ResumeCollectionChangeNotification();
            var arg = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset);
            this.OnCollectionChanged(arg);
        }
    }


}

关于c# - 将项目添加到 ObservableCollection 时的线程问题,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5781921/

相关文章:

c# - 将方法的参数带入自定义操作过滤器 MVC3 asp

c# - 将父类(super class)转换为子类

c# - Windows 身份模拟未持久保存到 Sql 可信连接中

c# - 接口(interface)与抽象类(在特定情况下)

c# - 确定集合是否属于 IEnumerable<T> 类型

使用多线程的无序映射的 C++ 填充

c# - 后台 worker 有限制吗?技术或常识

ios - 在主 ui 线程中加载 Realm 对象是否可以接受?

c# - 由于 EndpointDispatcher 的 AddressFilter 不匹配,无法在接收方处理带有 To '...' 的消息

html - 从 wcf 服务返回 html