Laden...

"Collection was modified" nach kleiner Codeanpassung

Erstellt von MysticEmpires vor 11 Jahren Letzter Beitrag vor 11 Jahren 1.197 Views
M
MysticEmpires Themenstarter:in
302 Beiträge seit 2004
vor 11 Jahren
"Collection was modified" nach kleiner Codeanpassung

verwendetes Datenbanksystem: Linq

Hallo,

ich hoffe ich bin in diesem Bereich richtig. Aber das ich Linq verwende dachte ich sollte das passen 😃

Ich hatte mal folgende Zeile im Programm:

//# Task starten
ServerList.ForEach(server => tasklist.Add(Task.Factory.StartNew(() => server.GetComputerWithLoggedOnUser(userId))));

diese habe ich durch folgenden Teil ersetzt:

//# Task starten
foreach (var tServer in ServerList.Select(server => Task.Factory.StartNew(() => server.GetComputerWithLoggedOnUser(userId))))
{
    tServer.LogExceptions();
    tasklist.Add(tServer);
}

Diese Änderung habe ich nur durchgeführt um die Funktion einer Extensions-Klasse zu benutzen die mit einem ContinueWith die Fehler im Task abfangen soll. Das funktioniert soweit auch ganz gut nur bekomme ich von meinen Usern nun öfters folgende Fehlermeldung:

Fehlermeldung:
Collection was modified; enumeration operation may not execute.

Das wundert mich nur etwas da sich ja an der Funktionsweise des Programm gar nichts geändert hat. Ich habe ja nur das hinzufügen zur "tasklist" etwas umgebaut um die Log-Funktion aufzurufen.

Jemand evtl. eine Idee wie sich der Fehler erklärt und was ich dagegen tun kann?

Gruß
Mystic

849 Beiträge seit 2006
vor 11 Jahren

Hallo,

schwierig zu sagen.. ändern deine Tasks vllt etwas an der ServerList ?
oder wenn LogExceptions vorher nicht aufgerufen wurde.. entfernst vllt einen Server der Exceptions hat?
Aber alles in den Wind geschossen.

Logged sich vllt während dieses Vorgangs wohl ein User ein/aus?

ich denke ein ToList an der richtigen Stelle wird hier wunder wirken 😃


foreach (var tServer in ServerList.Select(server => Task.Factory.StartNew(() => server.GetComputerWithLoggedOnUser(userId))).ToList())
{
tServer.LogExceptions();
tasklist.Add(tServer);
}

M
MysticEmpires Themenstarter:in
302 Beiträge seit 2004
vor 11 Jahren

Da der Fehler vorher nie aufgetreten ist schließe ich die Funktion "GetComputerWithLoggedOnUser" aus (sie ändert aber auch nichts an der Serverliste diese wird nur 1x initial angefasst). Die Funktion ruft im Endeffekt auch nur ne SQL-Query auf dem jeweiligen Server auf von daher auch nichts wildes.

Die LogExceptions macht auch nichts besonders:

public static void LogExceptions(this Task task)
{
    //# Kleinen Zusatz Task an einen Task hängen
    task.ContinueWith(t =>
    {
        //# Liegt kein Fehler vor? => raus hier
        if (t.Exception == null) 
            return;

        //# Exceptions zusammen fassen
        var aggException = t.Exception.Flatten();

        //# Fehler ins Log schreiben
        foreach (var exception in aggException.InnerExceptions)
            HelperClass.WriteErrorLog(exception);
    },
    TaskContinuationOptions.OnlyOnFaulted);
}

Ist das toList nötigt? Weil GetComputerWithLoggedOnUser lieft schon eine List<Class>

Gruß
Mystic

2.298 Beiträge seit 2010
vor 11 Jahren

Dachte mir gerade, die PN reicht aus jedoch möchte ich nun doch etwas schreiben:

Wie bereits per PN geschrieben hängt das ToList hier am ServerList.Select, nicht am server.GetComputerWithLoggedOnUser.

Der Grund warum hier auf ToList verwiesen wird, ist das in dem Fall eine Änderung an der Liste keine Auswirkung auf den Ablauf der Schleife hat. Entfernst du zum Beispiel einen server aus der Liste, schlägt der Iterationsvorgang bei deinem bisherigen Vorgang fehl. Durch das ToList, wird jedoch eine temporäre zusätzliche Liste aus den Elementen deines Selects erzeugt.

Wissen ist nicht alles. Man muss es auch anwenden können.

PS Fritz!Box API - TR-064 Schnittstelle | PS EventLogManager |

W
872 Beiträge seit 2005
vor 11 Jahren

Normalerweise muesstest Du anhand des Stacktrace sehen, bei welcher Collection es das Zugriffsproblem gibt - die saubere Lösung wäre es, daß Du diese Collection in eine ConcurrentCollection umwandelst, so daß die Iteratoren und Zugriffe durch das Framework synchronisiert werden.

M
MysticEmpires Themenstarter:in
302 Beiträge seit 2004
vor 11 Jahren

StackTrace ist wie folgt:

Fehlermeldung:
at System.Collections.Generic.List1.Enumerator.MoveNextRare() at System.Linq.Enumerable.WhereSelectListIterator2.MoveNext()
at Classes.AServers.GetComputerByName(String computerName, Boolean filterOld, Boolean removeDuplicates)
at MainForm.GetInterfaceInfos(String adComputername)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state, Boolean ignoreSyncCtx)
at System.Threading.ExecutionContext.Run(ExecutionContext executionContext, ContextCallback callback, Object state)
at System.Threading.ThreadHelper.ThreadStart()

Zeigt genau auf die Foreach-Zeile

W
872 Beiträge seit 2005
vor 11 Jahren

ServerList solltest Du in eine ConcurrentList umwandeln, das wird aber u.U. noch mehr Änderungen nach sich ziehen.
Eine Liste ist nicht threadsafe und wenn Du parallel foreach und add auf die gleiche Liste ausführst, dann gibt es die von "Collection was modified" Exception.