FileSystemWatcher - 从 ObservableCollection 添加和删除,C#

FileSystemWatcher - add and delete from ObservableCollection, C#

首先,我的代码:

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 个基于三个不同可观察集合的数据网格。 如果文件夹中添加了很多文件(或删除了很多文件),它会不时丢失一个文件并出现错误:

Collection was modified; enumeration operation may not execute

有什么线索可以找到丢失的文件吗?

您有明显的竞争条件:

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 线程以外的任何其他地方访问集合。

您也可以尝试同步访问集合,e.q。使用 lock 或使用 thread-safe 集合。这个 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
    }
}