Davasorus.Utility.DotNet.Services
2026.2.2.2
See the version list below for details.
dotnet add package Davasorus.Utility.DotNet.Services --version 2026.2.2.2
NuGet\Install-Package Davasorus.Utility.DotNet.Services -Version 2026.2.2.2
<PackageReference Include="Davasorus.Utility.DotNet.Services" Version="2026.2.2.2" />
<PackageVersion Include="Davasorus.Utility.DotNet.Services" Version="2026.2.2.2" />
<PackageReference Include="Davasorus.Utility.DotNet.Services" />
paket add Davasorus.Utility.DotNet.Services --version 2026.2.2.2
#r "nuget: Davasorus.Utility.DotNet.Services, 2026.2.2.2"
#:package Davasorus.Utility.DotNet.Services@2026.2.2.2
#addin nuget:?package=Davasorus.Utility.DotNet.Services&version=2026.2.2.2
#tool nuget:?package=Davasorus.Utility.DotNet.Services&version=2026.2.2.2
Davasorus.Utility.DotNet.Services
Davasorus.Utility.DotNet.Services provides a set of service/client abstractions for managing Windows services (start, stop, load, manipulate) in .NET 8+ applications. It is designed for use with dependency injection (DI). End users should only interact with the service interfaces; the services handle all client interactions, error handling, and logging.
Note: Only interact with the service interfaces (e.g.,
IStartServiceService,IStopServicesService,ILoadServicesService,IManipulateServiceService) in your application code. The service classes manage all communication with their respective client classes internally.
Breaking Changes in This Release
The root namespace has been renamed from Tyler.Utility.DotNet.Services.* to Davasorus.Utility.DotNet.Services.* to align with the published NuGet package identity. Consumers upgrading from any prior 2026.2.1.x version must update their using directives:
// Before
using Tyler.Utility.DotNet.Services.Configuration;
using Tyler.Utility.DotNet.Services.Windows_Services.Start_Services.Service;
// After
using Davasorus.Utility.DotNet.Services.Configuration;
using Davasorus.Utility.DotNet.Services.Windows_Services.Start_Services.Service;
No public API signatures changed. This is purely a namespace rename.
B1 — API contract modernization (this version)
DI lifetimes changed to Singleton
All registrations in AddWindowsServices now use TryAddSingleton. Consumers depending on Scoped or Transient resolution semantics will see one instance for the lifetime of the host. Consumers wanting to override the default implementations (e.g., for testing) can pre-register their implementation before calling AddWindowsServices — TryAddSingleton honors consumer pre-registrations.
IDisposable removed from all 8 interfaces
ILoadServicesClient, ILoadServicesService, IManipulateServiceClient, IManipulateServiceService, IStartServiceClient, IStartServiceService, IStopServicesClient, IStopServicesService no longer declare void Dispose();. Consumers using using var x = service; patterns must drop the using keyword:
// Before
using var loadService = scope.ServiceProvider.GetRequiredService<ILoadServicesService>();
var list = await loadService.GetServiceList("HOST");
// After
var loadService = scope.ServiceProvider.GetRequiredService<ILoadServicesService>();
var list = await loadService.GetServiceList("HOST");
Method return types changed to ServiceResult<T> / ServiceResult
Every public method now returns a ServiceResult (non-generic) or ServiceResult<T> (generic) wrapping the operation's outcome. The streaming method GetServiceListStreaming is the one exception — it retains IAsyncEnumerable<ServiceObj> and exceptions propagate to the consumer's await foreach.
// Before
var list = await loadService.GetServiceList("HOST");
if (list != null) { ... }
// After
var result = await loadService.GetServiceList("HOST");
if (result.IsSuccess && result.Value != null)
{
var list = result.Value;
// ...
}
// or, to handle failure:
if (!result.IsSuccess)
{
logger.LogError(result.Error, "List failed; reported to SQS: {Reported}", result.WasReportedToSqs);
}
ServiceResult carries IsSuccess, Error, and WasReportedToSqs. The WasReportedToSqs flag tells callers whether the package's catch-template successfully sent the error to SQS — useful for chain composition (don't double-report).
Manipulate methods now throw on null input (previously silent return false)
SetSpecifiedServiceOnMachineToEnabled, SetSpecifiedServiceOnMachineToDisabled, SetSpecifiedServiceOnMachineToManual and their Client-tier counterparts previously returned false silently when called with null or whitespace input. They now throw ArgumentException consistently with the other tiers. Inputs are validated via ArgumentException.ThrowIfNullOrWhiteSpace(...).
ServiceOptions changes
ServiceOperationTimeoutSecondshas been split into two fields:GeneralOperationTimeoutSeconds(default 60s) for general service operationsWebOperationTimeoutSeconds(default 30s) for IIS / w3svc operations
EnableErrorLoggingnow actually does work — setting it tofalseskips SQS reporting (previously the field was read by nothing)MaxRetryAttemptsnow actually drives retry behavior via Polly (previously the field was read by nothing)
ServiceOptionsBuilder method renames
// Before
services.AddWindowsServices(svc => svc.WithOperationTimeout(TimeSpan.FromSeconds(45)));
// After
services.AddWindowsServices(svc => svc.WithGeneralOperationTimeout(TimeSpan.FromSeconds(45)));
// or
services.AddWindowsServices(svc => svc.WithWebOperationTimeout(TimeSpan.FromSeconds(20)));
Polly retry on transient exceptions
Transient WMI / SCM / Registry exceptions (IOException, TimeoutException, SocketException, ServiceProcess.TimeoutException) now retry automatically per MaxRetryAttempts (default 3 retries). Non-transient exceptions (ArgumentException, UnauthorizedAccessException, etc.) do NOT retry. Worst-case wall-clock for a fully failing operation: (MaxRetryAttempts + 1) × timeout ≈ 4 × 60s = 240s for general operations under default settings.
If retry behavior is unwanted, set MaxRetryAttempts = 0 in options.
WaitForStatus is now an async polling loop
The blocking ServiceController.WaitForStatus(...) call inside Start/Stop has been replaced with an async polling loop (Internal/WaitForStatusAsyncHelper). The new loop polls every 500ms instead of blocking a thread-pool thread for the full timeout duration. Consumers observing thread-pool exhaustion under heavy Start/Stop load will see relief.
Features
- Start, stop, and manipulate Windows services on remote or local hosts
- Retrieve service lists and descriptions, with streaming support
- Strongly-typed async APIs
- OpenTelemetry distributed tracing via
ActivitySourceon all operations - Robust error handling and logging (ILogger + SQS integration)
- Designed for DI: all services registered as Singleton via
TryAddSingleton - Returns
ServiceResult<T>for every public method, with explicit success/failure outcomes and SQS-report tracking - Automatic retry on transient WMI/SCM/Registry exceptions via Polly resilience pipeline
- Async-polling wait loop replaces blocking
WaitForStatus, avoiding thread-pool exhaustion under heavy load - Hybrid validation: programmer errors throw, operational outcomes return
Dependency Injection Setup
Register all services using the AddWindowsServices() extension method:
using Davasorus.Utility.DotNet.Services.Configuration;
services.AddWindowsServices();
Or with fluent configuration:
services.AddWindowsServices(svc => svc
.WithGeneralOperationTimeout(TimeSpan.FromSeconds(60))
.WithWebOperationTimeout(TimeSpan.FromSeconds(30))
.WithRetryPolicy(3)
.WithErrorLogging(true));
This registers the following services automatically:
| Interface | Implementation | Lifetime |
|---|---|---|
IStartServiceService |
StartServiceService |
Singleton |
IStartServiceClient |
StartServiceClient |
Singleton |
IStopServicesService |
StopServicesService |
Singleton |
IStopServicesClient |
StopServicesClient |
Singleton |
ILoadServicesService |
LoadServicesService |
Singleton |
ILoadServicesClient |
LoadServicesClient |
Singleton |
IManipulateServiceService |
ManipulateServiceService |
Singleton |
IManipulateServiceClient |
ManipulateServiceClient |
Singleton |
ResiliencePipeline (shared) |
(built from ServiceOptions) |
Singleton |
ServiceOptions |
(via IOptions<> pipeline) |
Singleton |
Example Usage
Start a Service
public class MyServiceStarter
{
private readonly IStartServiceService _startService;
public MyServiceStarter(IStartServiceService startService)
{
_startService = startService;
}
public async Task StartAsync(string host, string serviceName)
{
await _startService.StartService(host, serviceName);
}
}
Stop a Service
public class MyServiceStopper
{
private readonly IStopServicesService _stopService;
public MyServiceStopper(IStopServicesService stopService)
{
_stopService = stopService;
}
public async Task StopAsync(string host, string serviceName)
{
await _stopService.StopService(host, serviceName);
}
}
Load Services List
public class MyServiceLoader
{
private readonly ILoadServicesService _loadService;
public MyServiceLoader(ILoadServicesService loadService)
{
_loadService = loadService;
}
public async Task<List<ServiceObj>?> GetServicesAsync(string host)
{
return await _loadService.GetServiceList(host);
}
}
Manipulate Service State
public class MyServiceManipulator
{
private readonly IManipulateServiceService _manipulateService;
public MyServiceManipulator(IManipulateServiceService manipulateService)
{
_manipulateService = manipulateService;
}
public async Task<bool> EnableServiceAsync(string host, string serviceName)
{
return await _manipulateService.SetSpecifiedServiceOnMachineToEnabled(host, serviceName);
}
}
API Overview
IStartServiceService
Task StartService(string hostName, string serviceName, TimeSpan? timeout = null)Starts the specified Windows service on the given host. Defaults to 60 seconds if no timeout is specified.Task StartWebServices(string hostName, TimeSpan? timeout = null)Starts all web-related Windows services on the specified host. Defaults to 30 seconds if no timeout is specified.
IStopServicesService
Task StopService(string hostName, string serviceName, TimeSpan? timeout = null)Stops the specified Windows service on the given host. Defaults to 60 seconds if no timeout is specified.Task StopWebServices(string hostName, TimeSpan? timeout = null)Stops all web-related Windows services on the specified host. Defaults to 30 seconds if no timeout is specified.
ILoadServicesService
Task<List<ServiceObj>?> GetServiceList(string hostName)Retrieves a list of Windows services from the specified host. Returns a list ofServiceObjinstances representing each service, ornullif retrieval fails.Task<string> GetServiceDescription(string hostName, string serviceName)Gets the description of a specific Windows service on the given host. Returns the service description as a string.IAsyncEnumerable<ServiceObj> GetServiceListStreaming(string hostName)Streams Windows services from the specified host as they are discovered. Returns an async stream ofServiceObjinstances.
IManipulateServiceService
Task<bool> SetSpecifiedServiceOnMachineToEnabled(string hostName, string serviceName)Enables the specified Windows service on the given host, setting its startup type to "Automatic". Returnstrueif successful.Task<bool> SetSpecifiedServiceOnMachineToDisabled(string hostName, string serviceName)Disables the specified Windows service on the given host, setting its startup type to "Disabled". Returnstrueif successful.Task<bool> SetSpecifiedServiceOnMachineToManual(string hostName, string serviceName)Sets the specified Windows service on the given host to "Manual" startup type. Returnstrueif successful.
Error Handling
All errors are logged via ILogger and reported to SQS. Service methods return null, false, or the exception message as appropriate, and log details for diagnostics. All operations are instrumented with OpenTelemetry ActivitySource spans for distributed tracing.
Requirements
- .NET 8.0 or .NET 10.0
License
MIT License
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net8.0 is compatible. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Davasorus.Utility.DotNet.Cache (>= 2026.2.2.13)
- Davasorus.Utility.Dotnet.Contracts.Collections (>= 2026.2.2.1)
- Davasorus.Utility.DotNet.Contracts.Types (>= 2026.2.2.1)
- Davasorus.Utility.DotNet.SQS (>= 2026.2.2.4)
- Davasorus.Utility.DotNet.Telemetry (>= 2026.2.2.5)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.8)
- Microsoft.Extensions.Http.Resilience (>= 10.6.0)
- Microsoft.Extensions.Logging (>= 10.0.8)
- System.Management (>= 10.0.8)
- System.ServiceProcess.ServiceController (>= 10.0.8)
-
net8.0
- Davasorus.Utility.DotNet.Cache (>= 2026.2.2.13)
- Davasorus.Utility.Dotnet.Contracts.Collections (>= 2026.2.2.1)
- Davasorus.Utility.DotNet.Contracts.Types (>= 2026.2.2.1)
- Davasorus.Utility.DotNet.SQS (>= 2026.2.2.4)
- Davasorus.Utility.DotNet.Telemetry (>= 2026.2.2.5)
- Microsoft.Extensions.Caching.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Configuration.Abstractions (>= 10.0.8)
- Microsoft.Extensions.Configuration.Binder (>= 10.0.8)
- Microsoft.Extensions.Http.Resilience (>= 10.6.0)
- Microsoft.Extensions.Logging (>= 10.0.8)
- System.Management (>= 10.0.8)
- System.ServiceProcess.ServiceController (>= 10.0.8)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 2026.2.3.1 | 41 | 6/1/2026 |
| 2026.2.2.14 | 46 | 5/31/2026 |
| 2026.2.2.13 | 91 | 5/23/2026 |
| 2026.2.2.12 | 96 | 5/19/2026 |
| 2026.2.2.11 | 93 | 5/18/2026 |
| 2026.2.2.10 | 97 | 5/18/2026 |
| 2026.2.2.9 | 87 | 5/18/2026 |
| 2026.2.2.8 | 90 | 5/17/2026 |
| 2026.2.2.7 | 94 | 5/17/2026 |
| 2026.2.2.6 | 99 | 5/17/2026 |
| 2026.2.2.5 | 89 | 5/17/2026 |
| 2026.2.2.4 | 84 | 5/16/2026 |
| 2026.2.2.3 | 88 | 5/16/2026 |
| 2026.2.2.2 | 95 | 5/16/2026 |
| 2026.2.2.1 | 94 | 5/15/2026 |
| 2026.2.1.3 | 149 | 4/16/2026 |
| 2026.2.1.2 | 2,237 | 4/9/2026 |
| 2026.2.1.1 | 344 | 4/1/2026 |
| 2026.1.3.5 | 153 | 3/29/2026 |
| 2026.1.3.4 | 186 | 3/29/2026 |