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?
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
}
}