Skip to content

SAIF.NetCore.WindowsService business rules

This page captures business rules enforced by this library.

WS-BR-01 Host is created once per process

Field Value
Statement The service host is created once per process and kept in a static property; Run builds it only when ServiceHost is null.
Trigger Run(args) is called.
Enforcement ServiceProgramRunnerBase.Run, ServiceHost (static property).
Outcome Reuses the existing host when already set. When reused, Create, the startup delay, and the configuration hooks are all skipped.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs

WS-BR-02 Optional startup delay blocks before hooks and Run

Field Value
Statement When configuration key DelayStart (milliseconds, integer) is present, startup blocks for that long after the host is built and before the configuration hooks and IHost.Run().
Trigger Create(args) runs.
Enforcement ServiceProgramRunnerBase.DelayStartup: Thread.Sleep(DelayStart).
Outcome If absent, there is no delay. If present, startup blocks synchronously on the main thread.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs

WS-BR-03 Configuration source stack is altered by re-added providers

Field Value
Statement The host reads appsettings.json (optional) and environment variables, in addition to and after, the Generic Host defaults, and runs as a Windows Service when started by the SCM.
Trigger CreateHostBuilder(args) runs.
Enforcement ServiceProgramRunnerBase.CreateHostBuilder.
Outcome The re-added sources override command-line args and appsettings.{Environment}.json, because later sources win*.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs

WS-BR-04 Error-handler hook runs before the logger is configured

Field Value
Statement The error-handler customization hook runs before the logger is configured.
Trigger Configure(host) runs.
Enforcement ServiceProgramRunnerBase.Configure.
Outcome Errors raised during ConfigureErrorHandler are not logged through the SAIF logger, because the logger is not yet configured.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs

WS-BR-05 Required hosting services must be registered

Field Value
Statement A SAIF Windows Service must have IWindowsServiceErrorHandler, ISaifWindowsServiceLogService, IEnvironmentClient, and IHttpJsonClient registered, and the logger is always configured at startup with the environment client and HTTP client.
Trigger Configure(host) / ConfigureLogger(host) runs.
Enforcement ServiceProgramRunnerBase.Configure/ConfigureLogger, using GetRequiredService.
Outcome Startup fails with InvalidOperationException if any required service is missing. Otherwise SaifWindowsServiceLog.Configure runs.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs

WS-BR-06 Worker loop is fixed-delay, not fixed-rate

Field Value
Statement A worker runs its task immediately, then waits TaskDelayMilliseconds (default 3000 ms) after each completion before the next run; runs never overlap.
Trigger The worker starts.
Enforcement ServiceWorker<T>.ExecuteAsync, TaskDelayMilliseconds.
Outcome Sequential runs with a fixed delay between them. The XML doc describing the delay as an "initial run delay" does not match this behavior.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs

WS-BR-07 Loop stops when the host's stopping token is cancelled

Field Value
Statement The worker loop stops when the host's stopping token is cancelled.
Trigger The host stops.
Enforcement ServiceWorker<T>.ExecuteAsync (and framework-standard BackgroundService semantics).
Outcome The loop exits, through TaskCanceledException if cancellation arrives during the delay.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs

WS-BR-08 Every task exception goes through error handling

Field Value
Statement No exception from a task run escapes the worker without going through the error-handling routine.
Trigger TaskRunner.Run throws.
Enforcement ServiceWorker<T>.RunTaskAsync.
Outcome HandleTaskError(ex) is always called.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs

WS-BR-09 Shutdown exemption requires an exact type match and an allowing predicate

Field Value
Statement An exception is exempt from shutdown only if its exact runtime type full name equals a registered ShutdownExemptExceptionType.Type and at least one matching entry's IsAllowed(ex) returns true; subclasses are not matched.
Trigger A task error occurs.
Enforcement ServiceWorker<T>.IsExceptionExemptFromShutdown. Entry validation (non-null, strict Exception subclass, default predicate true) comes from SAIF.NetCore.ServiceContracts's ShutdownExemptExceptionType.
Outcome true continues the service; false shuts it down.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs; src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ShutdownExemptExceptionType.cs

WS-BR-10 Non-exempt errors shut down the whole service

Field Value
Statement A non-exempt task error shuts down the whole service, allowing ShutdownTimeoutSeconds (default 30) for graceful stop.
Trigger A task error occurs and is not exempt.
Enforcement ServiceWorker<T>.HandleTaskError, ShutdownTimeoutSeconds.
Outcome ServiceHost.StopAsync(timeout) is called.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs

WS-BR-11 Error messages follow a fixed format

Field Value
Statement The error message passed to the handler is "An error occured in task: {TaskTitle}" plus a newline plus ex.Message, with "Service will shutdown." appended when not exempt.
Trigger A task error occurs.
Enforcement ServiceWorker<T>.BuildErrorMessage.
Outcome A consistent, overridable message text is produced for every task error.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs

WS-BR-12 Every error is reported before any shutdown

Field Value
Statement Every task error is reported to IWindowsServiceErrorHandler.HandleError before any shutdown, whether exempt or not.
Trigger A task error occurs.
Enforcement ServiceWorker<T>.HandleTaskError.
Outcome WindowsServiceErrorHandler.HandleError builds the error object, raises the BeginErrorHandling event first (default Handled = false, FireSpecialActions = true), then logs through ISaifWindowsServiceLogService.Error only if a subscriber has not set Handled = true, then -- only if FireSpecialActions is true -- fires each registered ISpecialErrorAction whose own independent fire-condition predicate allows it, logging any action that fails, and finally raises ErrorHandled. All of this completes before HandleTaskError evaluates whether to call StopAsync.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs; src/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/WindowsService/WindowsServiceErrorHandler.cs; src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/BeginErrorHandlingEventArgs.cs

WS-BR-13 Task runner must implement the worker's type parameter

Field Value
Statement A worker's task runner must implement the worker's type argument T (T : IServiceTaskRunner).
Trigger The worker is constructed.
Enforcement ServiceWorker<T>..ctor, generic constraint on T.
Outcome InvalidCastException is thrown at host start if the object does not implement T.
Source src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs

Notes

  • This library has no in-repo consumer and no test project; composition-pattern statements are otherwise unverified against a real consumer in this repository.

Note: Statements marked with * were inferred from code structure, naming conventions, or contextual evidence where direct confirmation was not available in source artifacts. Statements marked with ** could not be confirmed; treat as unknown until verified.

Sources

  • src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService.csproj
  • src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs
  • src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/ShutdownExemptExceptionType.cs
  • src/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/SAIF.NetCore.ServiceContracts/BeginErrorHandlingEventArgs.cs
  • src/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/SAIF.NetCore.ErrorHandler/WindowsService/WindowsServiceErrorHandler.cs