Skip to content

Configuration settings

This library reads exactly one configuration key directly, DelayStart, through IConfiguration. Its larger configuration impact is architectural: it defines the configuration-source stack for every Windows Service built on ServiceProgramRunnerBase, re-appending appsettings.json and unprefixed environment variables after the Generic Host defaults, which inverts standard .NET precedence. At startup it also triggers the Environment API reads of the resolved ISaifWindowsServiceLogService, a transitive dependency.

Configuration retrieval methods

Method Class / Interface Namespace Usage pattern
IConfiguration.GetValue<int?>(key) IConfiguration, resolved from IHost.Services with GetService Microsoft.Extensions.Configuration (+ .Binder 8.0.0) A single imperative read of DelayStart in private DelayStartup(IHost), called from Create after Build() and before Configure.
Host.CreateDefaultBuilder(args) Host Microsoft.Extensions.Hosting 8.0.0 Registers the default host and app-configuration providers.
IConfigurationBuilder.AddJsonFile("appsettings.json", optional: true) IConfigurationBuilder Microsoft.Extensions.Configuration.Json (transitive) Appends a second, non-reloading appsettings.json provider after the Generic Host defaults.
IConfigurationBuilder.AddEnvironmentVariables() IConfigurationBuilder Microsoft.Extensions.Configuration.EnvironmentVariables (transitive) Appends a second, unprefixed environment-variable provider; it is the last provider added, so it has the highest precedence.
IHostBuilder.UseWindowsService() IHostBuilder Microsoft.Extensions.Hosting.WindowsServices 8.0.0 Enables the Windows Service lifetime; when running under the SCM, also sets the content root to AppContext.BaseDirectory.
ISaifWindowsServiceLogService.Configure(IEnvironmentClient, IHttpJsonClient) (indirect) ServiceProgramRunnerBase.ConfigureLogger (virtual) SAIF.NetCore.Services.Contracts Resolves IEnvironmentClient and hands it to the logger, which then makes the Environment API reads listed under Transitive configuration.

appsettings.json and IConfiguration

Settings read through IConfiguration (appsettings.json, appsettings.<Environment>.json, environment variables, and any other configured provider).

Setting key Required Default value Retrieval Purpose Consuming class Source
DelayStart No — (absent → no delay) GetValue<int?> Blocks startup for the given number of milliseconds, after the host is built and before the error-handler/logger hooks and IHost.Run(); intended to give a developer time to attach a debugger to an installed service. 0 is no delay; -1 blocks forever (Timeout.Infinite); any other negative value throws ArgumentOutOfRangeException; a non-integer string throws InvalidOperationException. ServiceProgramRunnerBase.DelayStartup (private, non-overridable) src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs

DelayStart can be supplied by: an appsettings.json entry, the environment variable DelayStart (highest precedence, because of the re-added provider), or a process command-line argument (--DelayStart=<value>, /DelayStart=<value>, or DelayStart=<value>). For an installed service, the command-line form means the SCM binPath arguments, not the Services console "Start parameters" field — the README documents the wrong mechanism and the wrong key name (DelayStartup), so following it produces no delay*.

Configuration source stack defined by this library. CreateHostBuilder re-appends two providers after the Host.CreateDefaultBuilder defaults (host configuration, appsettings.json, appsettings.{Environment}.json, user secrets in Development, environment variables, command-line args):

Order Provider Added by
6 appsettings.json (optional, reloadOnChange: false) ServiceProgramRunnerBase.CreateHostBuilder
7 Environment variables (unprefixed) ServiceProgramRunnerBase.CreateHostBuilder

Resulting precedence (highest first): environment variables > appsettings.json > command line > user secrets > appsettings.{Environment}.json. Consequences: appsettings.{Environment}.json is effectively shadowed for any key also present in appsettings.json; command-line binPath arguments lose to appsettings.json and to environment variables; hot reload of appsettings.json is masked by the non-reloading duplicate provider, since provider 1 reloads but provider 6 (same file) does not.

Code-default tunables (not configuration-bound). ServiceWorker<T>.TaskDelayMilliseconds (default 3000) and ServiceWorker<T>.ShutdownTimeoutSeconds (default 30) are public settable properties with code defaults; consumers may set them from an Environment API value in their own worker constructor, but neither is bound to a configuration key by this library.

Environment variables

Variable Scope read Accepted values Default when unset Effect Source
DelayStart Process (inherited from machine/service-account environment) Integer milliseconds No delay Startup delay; highest precedence of all DelayStart sources because of the re-added provider. src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceProgramRunnerBase.cs
DOTNET_ENVIRONMENT Process Development, Staging, Production, or any name Production Selects appsettings.{Environment}.json (largely shadowed, see above) and enables user secrets / DI scope validation in Development. Microsoft.Extensions.Hosting CreateDefaultBuilder (framework-standard)
Any unprefixed Section__Key Process — — Overrides the same key from every other source (__ maps to :); applies to every consumer and transitive IConfiguration key, including COREAPIENABLED and AppSettings__ApplicationHost. ServiceProgramRunnerBase.CreateHostBuilder (re-added AddEnvironmentVariables())

Database connection configuration

Not applicable. The library makes no connection reads and resolves no IDBConnectionClient directly. See Transitive configuration for the one connection credential the companion registration can require.

Secure settings

Not read directly by this library. For completeness, the companion registration (SAIF.NetCore.ServiceExtensions.RegisterForWindowsService<T>) always calls RegisterDBConnectionClient(), which registers IDBConnectionClient as a lazy singleton factory. That factory — not ConnectionClient itself — performs the secure read the first time a consumer resolves IDBConnectionClient:

Setting key Encryption method Purpose Consuming class Source
dbconnectionclient:connectionservicecreds IEnvironmentClient.GetSecureSetting("connectionservicecreds", "dbconnectionclient", hash), where hash comes from a SystemHashService instance the factory constructs directly (new SystemHashService(typeof(ConnectionClient).Assembly), not DI-resolved) and calls .Get(SystemType.WebApp) on (hard-coded SystemType.WebApp, even for Windows Services*) Database connection credentials for IDBConnectionClient; the factory splits the returned string on \| and calls ConnectionClient.SetupClientCredentials(credentials[0], credentials[1]) RegisterDBConnectionClient (the factory lambda, in SAIF.NetCore.ServiceExtensions) -- ConnectionClient only receives the already-resolved credentials through SetupClientCredentials and does not perform the read itself src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs; src/SAIF.NetCore.DbConnectionClient/SAIF.NetCore.DBConnectionClient/SAIF.NetCore.DBConnectionClient/ConnectionClient.cs

Transitive configuration (from dependencies)

Settings required at runtime by services this library resolves but does not implement. These keys apply when the standard companion registration (SAIF.NetCore.ServiceExtensions.RegisterForWindowsService/RegisterForWindowsService<T>) supplies IWindowsServiceErrorHandler, ISaifWindowsServiceLogService, IEnvironmentClient, and IHttpJsonClient with their shipped implementations; a consumer who registers an equivalent, manually-implemented set of those four contracts instead may read a different set of settings, or none of these, depending on that implementation.

Setting key Method Source dependency Dependency type Purpose
tracingdefaults:loggingapiurl IEnvironmentClient.GetSettingValue SAIF.NetCore.Logging NuGet Package (same-repo) Logging API base URL, read by SaifWindowsServiceLog.Configure, which this library's ConfigureLogger hook invokes.
tracingdefaults:logroute IEnvironmentClient.GetSettingValue SAIF.NetCore.Logging NuGet Package (same-repo) Logging API route, read by the same call.
{ApplicationName}:logthreshold → fallback tracingdefaults:logthreshold IEnvironmentClient.HasSetting/GetSettingValue (read independently in two places: SaifLogBase's own LogThreshold property, and again inside ApiLoggerConfig's constructor for the API-sink-specific level) SAIF.NetCore.Logging NuGet Package (same-repo) Minimum log level threshold.
{ApplicationName}:netcorelogpattern → fallback tracingdefaults:netcorelogpattern IEnvironmentClient.HasSetting/GetSettingValue, read inside ApiLoggerConfig's constructor -- ApiLoggerConfig and the base Pattern/Level/LoggerType properties it populates are both declared on SaifLoggerConfig (SaifLoggerConfig.cs), a separate class from the logging-service base SaifLogBase SAIF.NetCore.Logging NuGet Package (same-repo) Output template pattern for the API logging sink.
tracingdefaults:logerrorroute — (not read for Windows Services) SAIF.NetCore.Logging NuGet Package (same-repo) Would be the error-specific logging route; SaifWindowsServiceLog never reads it, so LoggingApiErrorRoute stays null*.
IEnvironmentClient.TrackName / .ApplicationName Client properties SAIF.NetCore.EnvironmentClient NuGet Package (same-repo) Composes the EventLog source name; an empty TrackName throws on .First()*.
AppSettings:ApplicationHost IConfiguration bootstrap SAIF.NetCore.EnvironmentClient NuGet Package (same-repo) Required on developer machines to resolve IEnvironmentClient.
AppSettings:EnvApiDeveloperOverrideAllowed IConfiguration bootstrap SAIF.NetCore.EnvironmentClient NuGet Package (same-repo) Optional; default true, set false on non-domain-joined servers.
AppSettings:EnvApiDeveloperSettingsOverride IConfiguration bootstrap SAIF.NetCore.EnvironmentClient NuGet Package (same-repo) Optional; developer-only settings override file name.
EnvironmentClient:GetHostsByTrack:RetryCount IConfiguration bootstrap (GetHostsByTrackConfig) SAIF.NetCore.EnvironmentClient NuGet Package (same-repo) Optional bootstrap-resilience setting; number of retry attempts for GetHostsByTrack calls (default 3). Not read directly by this library -- it is read only inside IEnvironmentClient's own implementation.
EnvironmentClient:GetHostsByTrack:RetrySleepMs IConfiguration bootstrap (GetHostsByTrackConfig) SAIF.NetCore.EnvironmentClient NuGet Package (same-repo) Optional bootstrap-resilience setting; milliseconds to sleep between GetHostsByTrack retries (default 3000). Not read directly by this library.
COREAPIENABLED IConfiguration (unprefixed only; ASPNETCORE_COREAPIENABLED is not mapped in this non-web host*) SAIF.NetCore.EnvironmentClient NuGet Package (same-repo) Required for the secure environment client on servers.
EnvironmentApiUrl Environment.GetEnvironmentVariable (bypasses the IConfiguration stack) SAIF.NetCore.EnvironmentClient NuGet Package (same-repo) Required; cannot be supplied by appsettings.json or the command line.
dbconnectionclient:connectionservicecreds IEnvironmentClient.GetSecureSetting, secure SAIF.NetCore.ServiceExtensions's RegisterDBConnectionClient factory lambda (performs the read itself with a self-constructed SystemHashService, not an injected one) NuGet Package (same-repo) Required only if a consumer resolves IDBConnectionClient. The read and the factory both live in SAIF.NetCore.ServiceExtensions; ConnectionClient (in SAIF.NetCore.DBConnectionClient) only receives the resulting credentials via SetupClientCredentials and does not read the secure setting. On the non-secure RegisterEnvironmentClient() path, this call throws EnvironmentClientException ("Operation not allowed! This application is restricted from retrieving secure settings.") rather than failing with a null hash service.

Startup ordering note: DelayStart is read before ConfigureLogger resolves IEnvironmentClient, so a configured delay also postpones every transitive read above and the Environment API HTTP calls they trigger — useful when attaching a debugger to step through environment-client bootstrap.

Configuration dependencies

Dependency Type Required at Impact if missing
IConfiguration service (always registered by the Generic Host) startup (DelayStartup) Not expected to be missing; a null would throw NullReferenceException.
IWindowsServiceErrorHandler, ISaifWindowsServiceLogService, IEnvironmentClient, IHttpJsonClient services (resolved with GetRequiredService) startup (Configure/ConfigureLogger) InvalidOperationException if any is not registered; the recommended way to supply them is SAIF.NetCore.ServiceExtensions.RegisterForWindowsService/RegisterForWindowsService<T>, though ServiceProgramRunnerBase only requires the four contracts to be resolvable — an equivalent manual registration satisfies the same requirement without that call.
tracingdefaults:loggingapiurl, tracingdefaults:logroute, non-empty TrackName Environment API settings (transitive) startup (ConfigureLogger) Startup fails inside SaifWindowsServiceLog.Configure before the service runs.
EnvironmentApiUrl environment variable (transitive) startup IEnvironmentClient resolution fails before ConfigureLogger can run.

The README documents a debugging technique (/DelayStartup=[ms] as a Services-console start parameter) that does not work: the code reads DelayStart, not DelayStartup, and Windows Service start parameters likely do not reach process Main(string[] args) at all*.

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/ServiceProgramRunnerBase.cs
  • src/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/SAIF.NetCore.WindowsService/ServiceWorker.cs
  • src/SAIF.NetCore.WindowsService/README.md (documents the obsolete /DelayStartup=[ms] Services-console parameter and the wrong key name)
  • src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SaifWindowsServiceLog.cs (logging sink/setting reads triggered by ConfigureLogger)
  • src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SaifLogBase.cs (base-class LogThreshold resolution via its own independent logthreshold read, and logger-override application; declares the abstract BuildStandardLoggerConfigs/InitializeLoggerInfoLookups that populate Patterns from each SaifLoggerConfig subclass)
  • src/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SAIF.NetCore.Logging/SaifLoggerConfig.cs (ApiLoggerConfig's constructor is the class that actually reads {ApplicationName}:netcorelogpattern/tracingdefaults:netcorelogpattern and the per-sink logthreshold fallback; EventLogConfig uses a fixed DefaultPattern instead and reads no setting)
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/BaseEnvironmentClient.cs, SecureEnvironmentClient.cs (settings resolution behind IEnvironmentClient)
  • src/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/SAIF.NetCore.EnvironmentClient/ClientConfigs/GetHostsByTrackConfig.cs (RetryCount/RetrySleepMs defaults)
  • src/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/SAIF.NetCore.ServiceExtensions/IServiceCollectionExtensions.cs (RegisterDBConnectionClient factory: constructs its own SystemHashService, performs the secure credential read, and calls ConnectionClient.SetupClientCredentials)
  • src/SAIF.NetCore.DbConnectionClient/SAIF.NetCore.DBConnectionClient/SAIF.NetCore.DBConnectionClient/ConnectionClient.cs (IDBConnectionClient implementation; receives credentials via SetupClientCredentials but does not itself perform the secure-setting read)