1

first of all, my code:

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);
        }

    }

Here i have 3 dataGrid based on three different observable collections. if a add a lot of files (or delete a lot of files) of the folder, it misses a file from time to time with the error:

Collection was modified; enumeration operation may not execute

Any clues how to catch the missing files?

sujith karivelil
  • 28,671
  • 6
  • 55
  • 88
Tommehh
  • 872
  • 1
  • 17
  • 44
  • 1
    This message means that you are modifying your iterator collection, during the iteration. Maybe you have to create a copy of your collection, and copy de added files to the new collection(same proccess with deleted files). – Jose Jan 12 '16 at 13:26
  • At which line exception occurs? – Sinatr Jan 12 '16 at 13:29
  • stacktrace says at this line:machineOrderAdded = viewModel.MachineOrdersActive.FirstOrDefault(x => x.Filename == e.Name); – Tommehh Jan 12 '16 at 13:34
  • This is because you have chance to enumerate collection (inside linq query) at exact moment when UI thread delete something, collection modified and you have your exception. It's design issue. In MVVM only `ViewModel` can update `ObservableCollection`, Model (handling `FileSystemWatcher`) should either operate with own `List` or simply send notifications to ViewModel. In your case easy fix is do not ever enumerate collection outside of `Invoke` delegate. – Sinatr Jan 12 '16 at 13:37
  • "In your case easy fix is do not ever enumerate collection outside of Invoke delegate" how do you mean that? – Tommehh Jan 12 '16 at 13:39
  • 2
    Threading is eating your lunch. Instead of peppering this code with BeginInvoke calls, only use **one** and have the target method execute the switch-statement. – Hans Passant Jan 12 '16 at 14:01

1 Answers1

3

You have obvious race condition:

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

To fix it move everything inside Invoke:

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

And so on for all cases, avoid accessing collection from anywhere else but UI thread.

You can also try to synchronize access to collection, e.q. using lock or using thread-safe collection. This will not work with ObservableCollection.

As per @HansPassant comment, you can simply invoke FileSystemWatcher events directly into UI thread and do all switch/case there.

// 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
    }
}
Community
  • 1
  • 1
Sinatr
  • 20,892
  • 15
  • 90
  • 319
  • i dont really get it. Dispatcher.InvokeAsync(() doesnt exists somehow.. and everything i have is an observablecollection – Tommehh Jan 12 '16 at 13:51
  • 2
    It [exists in net4.5+](https://msdn.microsoft.com/en-us/library/system.windows.threading.dispatcher.invokeasync(v=vs.110).aspx), use `BeginInvoke` then. – Sinatr Jan 12 '16 at 13:55