---
title: Service registration
summary: Services, options, startup base classes, middleware, and hosted services this library contributes to a consuming application.
status: draft
owner: SAIF Framework Platform Team
last_reviewed: 2026-10-02
library: SAIF.NetCore.WindowsService
reference_type: package
---

# Service registration

This library registers **no services of its own** — it exposes two startup base classes a consumer inherits (`ServiceProgramRunnerBase` for the program entry point, `ServiceWorker<T>` for the recurring task loop) and resolves four services from the DI container that only `SAIF.NetCore.ServiceExtensions`'s `RegisterForWindowsService`/`RegisterForWindowsService<T>` actually registers; a consuming Windows Service application must reference and call that companion method, or supply the same contracts itself through manual registration.

## Registration extension catalog

This library defines no `IServiceCollection`/`IHostBuilder` extension methods itself. The extension methods below are owned and shipped by `SAIF.NetCore.ServiceExtensions`; they are documented here because this library's startup base classes and `ServiceWorker<T>` require their output to be present in the DI container at runtime. See [SAIF.NetCore.ServiceExtensions overview](../saif-netcore-serviceextensions/overview.md) for that library's own documentation. Using `RegisterForWindowsService`/`RegisterForWindowsService<T>` is the recommended way to satisfy those runtime contracts, but it is not the only way: `ServiceWorker<T>` and `ServiceProgramRunnerBase` only require that `IWindowsServiceErrorHandler`, `ISaifWindowsServiceLogService`, `IEnvironmentClient`, and `IHttpJsonClient` be resolvable from the container, however they get there — an equivalent set of manual registrations satisfies the same contracts without calling this specific extension method.

| Extension method | Hosting context | Parameters | Source |
| --- | --- | --- | --- |
| `RegisterForWindowsService<T>(this IServiceCollection services, bool registerSecureEnvironmentClient = true) where T : class` | worker (Windows service) | `registerSecureEnvironmentClient` (default `true`): when `true`, calls `RegisterSecureEnvironmentClient(typeof(T))`; when `false`, calls `RegisterEnvironmentClient()` (non-secure, registers `ISystemHashService` as a factory returning `null`). Always also registers `ISaifWindowsServiceLogService`, `IWindowsServiceErrorHandler`, and calls `RegisterDBConnectionClient()`. | src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs |
| `RegisterForWindowsService(this IServiceCollection services)` | worker (Windows service) | No parameters; delegates to `RegisterForWindowsService<object>(false)` — always takes the **non-secure** `EnvironmentClient` path. | src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs |

> Overload-selection caveat: `RegisterDBConnectionClient()` is called unconditionally by both overloads, always registering `IDBConnectionClient` as a lazy singleton factory. That factory lambda (in `SAIF.NetCore.ServiceExtensions`, not inside `ConnectionClient`) constructs its own `SystemHashService(typeof(ConnectionClient).Assembly)` — it does not use the container-registered `ISystemHashService` — gets its hash, and calls `IEnvironmentClient.GetSecureSetting` to retrieve and split the connection credential before calling `ConnectionClient.SetupClientCredentials`. On the non-secure path — either the parameterless `RegisterForWindowsService()` overload, or `RegisterForWindowsService<T>(false)` — `IEnvironmentClient` resolves to the non-secure `BaseEnvironmentClient`, whose `GetSecureSetting` throws `EnvironmentClientException` ("Operation not allowed! This application is restricted from retrieving secure settings.") rather than failing with a null-reference error; the registered-but-unused `null` `ISystemHashService` factory is not involved in this failure. Prefer the generic overload with its `true` default (or pass `true` explicitly) whenever `IDBConnectionClient` will be used.

## Service registration catalog

These are the registrations made by the companion `SAIF.NetCore.ServiceExtensions.RegisterForWindowsService<T>`/`RegisterForWindowsService` calls this library depends on at runtime — none of them are registered by this library itself. A consumer may instead register the same contracts manually if it does not want the convenience call.

| Service type | Implementation | Lifetime | Registered by | Condition | Source |
| --- | --- | --- | --- | --- | --- |
| `IEnvironmentClient` (+ `ISystemWrapper`, `IHostResolver`, `IHttpJsonClient` transient, `ISystemHashService`) | `SecureEnvironmentClient` / `BaseEnvironmentClient` | Singleton (`IHttpJsonClient` transient) | `RegisterSecureEnvironmentClient(typeof(T))` / `RegisterEnvironmentClient()` | Gated by `registerSecureEnvironmentClient`; the non-generic `RegisterForWindowsService()` overload passes `false`, registering `ISystemHashService` as a factory returning `null`. | src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs |
| `ISaifWindowsServiceLogService` | `SaifWindowsServiceLog` | Singleton | `RegisterForWindowsService<T>` | Always | src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs |
| `IWindowsServiceErrorHandler` | `WindowsServiceErrorHandler` | Singleton | `RegisterForWindowsService<T>` | Always | src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs |
| `IDBConnectionClient` | factory -> `ConnectionClient` (in `SAIF.NetCore.DbConnectionClient`) with secure credentials | Singleton | `RegisterDBConnectionClient()` | Always | src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs; src/SAIF.NetCore.DbConnectionClient/SAIF.NetCore.DBConnectionClient/SAIF.NetCore.DBConnectionClient/ConnectionClient.cs |

## Startup base classes

Base classes a consuming application inherits to bootstrap its host.

| Base class | Inherits | Consumer type | Must implement | May override | Source |
| --- | --- | --- | --- | --- | --- |
| `ServiceProgramRunnerBase` | `object` | Windows Service entry point (`Program`) | None (no abstract members) | `Run(string[])`: creates the host once, then calls `IHost.Run()`. `Create(string[])`: builds the host, applies the optional delay, calls `Configure`. `CreateHostBuilder(string[])`: `CreateDefaultBuilder` + re-added `appsettings.json` + environment variables + `UseWindowsService()`. `ConfigureErrorHandler(IWindowsServiceErrorHandler)`: empty by default. `Configure(IHost)`: runs the error-handler hook, then `ConfigureLogger`. `ConfigureLogger(IHost)`: calls `ISaifWindowsServiceLogService.Configure(env, http)`. | src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs |
| `ServiceWorker<T>` (`where T : IServiceTaskRunner`) | `Microsoft.Extensions.Hosting.BackgroundService` | One consumer worker class per scheduled task | None (no abstract members) | `ExecuteAsync` (override): fixed-delay loop, immediate first run. `RunTaskAsync`: try/catch around `TaskRunner.Run`. `HandleTaskError`: exemption check, error-handler call, host stop. `BuildErrorMessage`: standard error-message text. Non-virtual: `IsExceptionExemptFromShutdown`. | src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs |

## Feature flags

Properties a consumer overrides, and environment variables a base class reads, that change registration or middleware behavior. Environment variables are also listed in [Configuration](configuration.md).

| Name | Kind | Default | Effect | Source |
| --- | --- | --- | --- | --- |
| `TaskDelayMilliseconds` | Property (`ServiceWorker<T>`) | 3000 | Delay after each task run, before the next iteration. | src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs |
| `ShutdownTimeoutSeconds` | Property (`ServiceWorker<T>`) | 30 | Graceful-stop timeout passed to `ServiceHost.StopAsync` on a fail-stop error. | src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs |
| `DelayStart` | Environment variable / configuration key (process) | unset (no delay) | Blocks startup for N ms before the configuration hooks and `IHost.Run()`. | src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs |

## Hosted services and health checks

| Type | Kind | Registered by | Behavior | Source |
| --- | --- | --- | --- | --- |
| `ServiceWorker<T>` (abstract; consumer subclasses) | `BackgroundService` | The consumer, with `services.AddHostedService<TWorker>()` — this library does not register it | Runs `T.Run(stoppingToken)` immediately, then every `TaskDelayMilliseconds` after completion. Catches all errors, reports them to `IWindowsServiceErrorHandler`, and stops the host unless the exception's exact runtime type is registered as shutdown-exempt and its predicate allows it. | src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs |

There are no health checks or ping endpoints.

## Required-but-not-registered services

Services this library's types resolve that none of its own methods add. The consuming application, or another library, must register them — in practice, by referencing `SAIF.NetCore.ServiceExtensions` and calling `RegisterForWindowsService`/`RegisterForWindowsService<T>`.

| Service type | Consumer | Expected source |
| --- | --- | --- |
| `IServiceTaskRunner` (concrete `T`) | `ServiceWorker<T>` constructor | The consumer app (e.g. `AddTransient<IMyRunner, MyRunner>()`) |
| `IWindowsServiceErrorHandler` | `ServiceWorker<T>` constructor; `ServiceProgramRunnerBase.Configure` (`GetRequiredService`) | `SAIF.NetCore.ServiceExtensions`'s `RegisterForWindowsService` → `SAIF.NetCore.ErrorHandler`'s `WindowsServiceErrorHandler` (singleton) |
| `ISaifWindowsServiceLogService` | `ServiceProgramRunnerBase.ConfigureLogger` | `RegisterForWindowsService` → `SAIF.NetCore.Logging`'s `SaifWindowsServiceLog` (singleton) |
| `IEnvironmentClient` | `ServiceProgramRunnerBase.ConfigureLogger` | `RegisterForWindowsService` → `SAIF.NetCore.EnvironmentClient`'s `BaseEnvironmentClient`/`SecureEnvironmentClient` (singleton) |
| `IHttpJsonClient` | `ServiceProgramRunnerBase.ConfigureLogger` | `RegisterForWindowsService` → `SAIF.NetCore.HttpClients`'s `HttpJsonClient` (transient) |
| `IConfiguration` | `ServiceProgramRunnerBase.DelayStartup` | Generic Host (always registered automatically) |

## Related documentation
- [Index](index.md)
- [Overview](overview.md)
- [Architecture](architecture.md)
- [Dependencies](dependencies.md)
- [Configuration](configuration.md)
- [Operations and processes](operations-processes.md)
- [Business rules](business-rules.md)
- [API reference](api/index.md)

## 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.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs`
- `src/SAIF.NetCore.DbConnectionClient/SAIF.NetCore.DBConnectionClient/SAIF.NetCore.DBConnectionClient/ConnectionClient.cs`
