【问题标题】:FileSystemWatcher - add and delete from ObservableCollection, C#FileSystemWatcher - 从 ObservableCollection 中添加和删除,C#
【发布时间】:2016-01-12 13:06:21
【问题描述】:

首先,我的代码:

private void OnChangedActive(object source, FileSystemEventArgs e)
    {
        try
        {
            switch (e.ChangeType)
            {
                case WatcherChangeTypes.Created:
                    if (File.Exists(e.FullPath))
                    {
                        MachineOrder machineOrderAdded;

                        machineOrderAdded = viewModel.MachineOrdersActive.FirstOrDefault(x => x.Filename == e.Name);
                        if (machineOrderAdded != null)
                            this.Dispatcher.BeginInvoke(new Action(() => viewModel.MachineOrdersActive.Remove(machineOrderAdded)));

                        machineOrderAdded = viewModel.MachineOrdersProductionpool.FirstOrDefault(x => x.Filename == e.Name);
                        if (machineOrderAdded != null)
                            this.Dispatcher.BeginInvoke(new Action(() => viewModel.MachineOrdersProductionpool.Remove(machineOrderAdded)));

                        machineOrderAdded = viewModel.MachineOrdersInProduction.FirstOrDefault(x => x.Filename == e.Name);
                        if (machineOrderAdded != null)
                            this.Dispatcher.BeginInvoke(new Action(() => viewModel.MachineOrdersInProduction.Remove(machineOrderAdded)));


                        this.Dispatcher.BeginInvoke(new Action(() => viewModel.MachineOrdersActive.Add(mainController.generateMachineOrder(e.FullPath))));
                    }

                    break;
                case WatcherChangeTypes.Deleted:

                    MachineOrder machineOrder;
                    String message = "";

                    //ÜBERPRÜFEN, OB SIE IM AKTIVORDNER IST
                    machineOrder = viewModel.MachineOrdersActive.FirstOrDefault(x => x.Filename == e.Name);

                    if (machineOrder != null)
                    {
                        this.Dispatcher.BeginInvoke(new Action(() => viewModel.MachineOrdersActive.Remove(machineOrder)));
                        message = String.Format("Die Datei {0} existiert nicht mehr. Der dazugehörige Auftrag wurde von den aktiven Aufträgen entfernt.", machineOrder.Filename);
                        this.Dispatcher.BeginInvoke(new Action(() => setStatus(message, Level.INFO)));
                        Logger.getInstance().writeLogEntry(Logger.LogLevel.INFO, message, null);
                        break;
                    }


                    //ÜBERPRÜFEN, OB SIE IM FERTIGUNGSPOOL IST
                    machineOrder = viewModel.MachineOrdersProductionpool.FirstOrDefault(x => x.Filename == e.Name);

                    if (machineOrder != null)
                    {
                        this.Dispatcher.BeginInvoke(new Action(() => viewModel.MachineOrdersProductionpool.Remove(machineOrder)));
                        message = String.Format("Die Datei {0} existiert nicht mehr. Der dazugehörige Auftrag wurde aus dem Fertigungspool entfernt.", machineOrder.Filename);
                        this.Dispatcher.BeginInvoke(new Action(() => setStatus(message, Level.INFO)));
                        Logger.getInstance().writeLogEntry(Logger.LogLevel.INFO, message, null);
                        break;
                    }


                    //ÜBERPRÜFEN, OB SIE IM FERTIGUNGSSPEICHER IST
                    machineOrder = viewModel.MachineOrdersInProduction.FirstOrDefault(x => x.Filename == e.Name);

                    if (machineOrder != null)
                    {
                       message = String.Format("Die Datei {0} existiert nicht mehr. Der dazugehörige Auftrag wurde nicht entfernt, da er sich bereits in Produktion befindet", machineOrder.Filename);
                       this.Dispatcher.BeginInvoke(new Action(() => setStatus(message, Level.INFO)));
                       Logger.getInstance().writeLogEntry(Logger.LogLevel.INFO, message, null);
                       break;
                    }


                    //NACHRICHT AUSGEBEN
                    if (String.IsNullOrEmpty(message))
                    {
                        message = String.Format("Die Datei {0} existiert nicht mehr. Der dazugehörige Auftrag wurde nicht gefunden.", machineOrder.Filename);
                        this.Dispatcher.BeginInvoke(new Action(() => setStatus(message, Level.INFO)));
                        Logger.getInstance().writeLogEntry(Logger.LogLevel.INFO, message, null);
                    }




                    break;
                default:
                    break;
            }   
        }
        catch (Exception ex)
        {
            this.Dispatcher.BeginInvoke(new Action(() => setStatus(ex.Message, Level.ERROR)));
            Logger.getInstance().writeLogEntry(Logger.LogLevel.INFO, ex.Message, ex.StackTrace);
        }

    }

这里我有 3 个基于三个不同可观察集合的 dataGrid。 如果一个文件夹添加了很多文件(或删除了很多文件),它会时不时丢失一个文件并出现错误:

Collection was modified; enumeration operation may not execute

任何线索如何捕捉丢失的文件?

【问题讨论】:

  • 此消息表示您正在迭代期间修改迭代器集合。也许您必须创建收藏的副本,并将添加的文件复制到新收藏中(与已删除文件相同的过程)。
  • 哪一行出现异常?
  • stacktrace 在这一行说:machineOrderAdded = viewModel.MachineOrdersActive.FirstOrDefault(x => x.Filename == e.Name);
  • 这是因为您有机会在 UI 线程删除某些内容、修改集合并且您遇到异常的确切时刻枚举集合(在 linq 查询中)。是设计问题。在 MVVM 中,只有 ViewModel 可以更新 ObservableCollection,模型(处理 FileSystemWatcher)应该使用自己的 List 操作或简单地向 ViewModel 发送通知。在您的情况下,简单的解决方法是永远不要枚举 Invoke 委托之外的集合。
  • 线程正在吃你的午餐。不要在此代码中添加 BeginInvoke 调用,只需使用 one 并让目标方法执行 switch 语句。

标签: c# wpf filesystemwatcher


【解决方案1】:

你有明显的竞争条件:

machineOrderAdded = viewModel.MachineOrdersActive.FirstOrDefault(x => x.Filename == e.Name);
if (machineOrderAdded != null)
    this.Dispatcher.BeginInvoke(new Action(() => viewModel.MachineOrdersActive.Remove(machineOrderAdded)));

要修复它,请将 Invoke 中的所有内容移动:

Dispatcher.InvokeAsync(() =>
{
    var machineOrderAdded = viewModel.MachineOrdersActive.FirstOrDefault(x => x.Filename == e.Name);
    if(machineOrderAdded != null)
        viewModel.MachineOrdersActive.Remove(machineOrderAdded);
});

等等,对于所有情况,避免从除 UI 线程之外的任何其他地方访问集合。

您也可以尝试同步对集合的访问,例如使用lock 或使用线程安全集合。这个will not workObservableCollection

根据@HansPassant 的评论,您可以简单地将FileSystemWatcher 事件直接调用到UI 线程中,并在那里执行所有switch/case

// using reinvoke pattern, you can invoke another method to avoid "double-checking"
void OnChangedActive(object source, FileSystemEventArgs e)
{
    if (!Dispatcher.CheckAccess())
        Dispatcher.InvokeAsync(() => OnChangedActive(sender, e)); // sorry for InvokeAsync :)
    else
    {
        ... // your code goes here without need to use invoke
    }
}

【讨论】:

  • 我真的不明白。 Dispatcher.InvokeAsync(() 以某种方式不存在.. 我所拥有的一切都是 observablecollection
  • exists in net4.5+,然后使用BeginInvoke
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多