void WorkItemCreating()

Bevor ein (noch nicht laufendes) Work-Item vom Work-Manager eingeplant oder aktualisiert wird, wird die Methode aufgerufen. Darin kann das Work-Item eigene Eigenschaften berechnen, die mehr Informationen brauchen als dem Konstruktor zur Verfügung stehen.

Die Standardimplementierung ist leer.

void WorkItemShouldFinish()

Diese Methode wird aufgerufen, wenn ein laufendes Work-Item abgebrochen werden soll. Das gibt dem Work-Item die Möglichkeit, asynchron auf ein solches Event zu reagieren, und beispielsweise ein ManualResetEvent zu setzen.

Die Standardimplementierung schreibt eine Warnung über dieses Ereignis ins Applikationsprotokoll, falls dieses aktiv ist.

void WorkItemFinished()

Diese Methode wird für jedes Work-Item genau einmal ausgeführt, das der Work-Manager einmal zu Gesicht bekommen hat. Was dann tatsächlich passiert, ist der Eigenschaft “State” zu entnehmen:

  • Finished Die Methode Run ist ordnungsgemäß zurückgekehrt oder das Work-Item wurde zwar abgebrochen, aber es war sowieso gerade fertig (CurrentProgress == MaxProgress)
  • Removed Das Work-Item war schon einmal eingeplant und wurde, bevor es starten konnte abgebrochen.
  • Cancelled Das Work-Item hat auf eine Abbruchanforderung durch den Benutzer reagiert und sich selbst beendet.
  • Timeout Das Work-Item hat die maximale Verarbeitungsdauer überschritten und sich daraufhin beendet.
    Die Standardimplementierung würde hier eine automatische Neueinplanung versuchen, solange MaxNumberOfRestarts noch nicht erreicht ist.
  • Killed Das Work-Item hat die maximale Verarbeitungsdauer überschritten und sich daraufhin binnen der gesetzten Nachfrist nicht beendet.
  • Error Die Run-Methode ist mit einer unbehandelten Ausnahmen abgebrochen. Diese steht in ExceptionDuringRun.
    Die Standardimplementierung protokolliert die Ausnahme mit LogCriticalError und versucht einen Neueinplanung, solange MaxNumberOfRestarts noch nicht erreicht ist.
  • Aborted Die Ausführung wurde durch einen Anwendungsneustart unerwartet unterbrochen. Dieser Aufruf erfolgt natürlich erst nach dem Neustart der Anwendung.
    Die Standardimplementierung versucht hier immer einen Neustart, unabhängig von MaxNumberOfRestarts.

Wenn sich das Work-Item nach der Ausführung der Methode neu eingeplant hat, wechselt der Status von Finished auf Reschedule, von Timeout auf TimeoutRetry und von Error auf ErrorRetry.

Überschriebene Implementierungen sollten im allgemeinen base.WorkItemFinished() in den Fällen aufrufen, die sie nicht selbst explizit behandelt haben. Nur dadurch ist sichergestellt, dass Worker z.B. nach einem Anwendungsneustart auch wirklich wieder starten.

Ferner sollte die Methode nicht selbst Save aufrufen. Das würde die transaktionale Integrität brechen. Das Work-Item wird danach immer automatisch gespeichert.

Ebenfalls sollten keine aufwändige Berechnungen in der Methode erfolgen. Für die Ausführung steht nur ein enges Zeitkontingent von einer Minute zur Verfügung. Danach wird der Worker unwiderruflich abgebrochen. Auch ein wiederholt eingeplanter Worker würde nach einem solchen Abbruch nie wieder laufen.

Beispiel, um nach einem (bestätigten) Timeout beliebig oft und ohne Karenzzeit dazwischen neu zu starten – das entspricht logisch Thread.Yield():

protected override void WorkItemFinished()
{
    if (State == EnumWorkItemState.Timeout)
        AddSuccessor(TimeSpan.Zero);
    else
        base.WorkItemFinished();
}

InitLogger()

Diese Methode wird vor der eigentlichen Ausführung eines Work-Items aufgerufen, wenn IsLoggingEnabled aktiviert ist. Alternativ kann die Methode auch selbst aufgerufen werden, wenn eine verzögerte bzw. bedingte Anlage des Protokolls gewünscht ist. Dann muss LoggerGuid leer sein.

Die Standardimplementierung legt ein neues Applikationsprotokoll an, wenn LoggerGuid leer ist oder öffnet ein bestehendes Protokoll wenn einer LoggerGuid angegeben wurde. Nachher ist LoggerGuid immer gefüllt. Dadurch ist sichergestellt, dass nach Neustarts im selben Protokoll fortgefahren wird.

Die Methode kann überschrieben werden, um abweichende Logging-Verfahren zu implementieren. beispielsweise täglich rollierende Logs.

CreateSuccsessor()

Das ist faktisch eine Klon-Methode. Sie erstellt eine Kopie des Work-Items. Dabei werden alle persistierten Eigenschaften des Work-Items übernommen, mit folgenden Ausnahmen:

  • State ist immer Idle (Standard für neue Items)
  • ScheduledStartTime wird auf die aktuelle Uhrzeit gesetzt, also sofort einplanen.
  • NumberOfStarts wird nicht übernommen. Das muss explizit selbst gemacht werden, wenn erwünscht ist, dass bei dieser Neueinplanung der Countdown für MaxNumberOfRestarts läuft.

Eigene Worker können diese Methode überschreiben, um ein abweichendes Verhalten zu implementieren. Das gilt dann für alle per AddSuccessor erzeugten Nachfolger einschließlich automatischer Neustarts nach Fehlern.

Beim Überschreiben sollte immer zuerst die Standardimplementierung base.CreateSuccessor() aufgerufen werden.