Josupeit.Practices.Services
1.0.24
Prefix Reserved
dotnet add package Josupeit.Practices.Services --version 1.0.24
NuGet\Install-Package Josupeit.Practices.Services -Version 1.0.24
<PackageReference Include="Josupeit.Practices.Services" Version="1.0.24" />
<PackageVersion Include="Josupeit.Practices.Services" Version="1.0.24" />
<PackageReference Include="Josupeit.Practices.Services" />
paket add Josupeit.Practices.Services --version 1.0.24
#r "nuget: Josupeit.Practices.Services, 1.0.24"
#:package Josupeit.Practices.Services@1.0.24
#addin nuget:?package=Josupeit.Practices.Services&version=1.0.24
#tool nuget:?package=Josupeit.Practices.Services&version=1.0.24
Josupeit.Practices.Services
Managing a service lifecycle correctly is surprisingly subtle. State transitions need to be valid (you cannot resume a service that was never paused), and callers need to see consistent intermediate states while a transition is in progress. This package provides abstract base classes that handle that bookkeeping, leaving you to focus on what the service actually does.
dotnet add package Josupeit.Practices.Services
All types live in the Josupeit.Practices.Services namespace, shared with the Abstractions package.
How to implement a service
Synchronous service
Inherit from Service and implement the two abstract lifecycle hooks. The base class manages the State property and validates transitions. Calling Start() on a service that is already running is a no-op; a conflicting call such as Resume() on a running service throws an InvalidOperationException rather than silently misbehaving. Lifecycle calls are not synchronized; concurrent Start/Stop/Pause/Resume calls on the same instance must be serialized by the caller.
public sealed class DataSyncService : Service
{
private Timer? _timer;
protected override void OnStart()
{
_timer = new Timer(_ => Synchronize(), null, TimeSpan.Zero, TimeSpan.FromMinutes(1));
}
protected override void OnStop()
{
_timer?.Dispose();
_timer = null;
}
}
var svc = new DataSyncService();
svc.Start();
Console.WriteLine(svc.State); // Running
svc.Stop();
Adding pause support
Pausing is opt-in. Override CanPause to return true, then override the two pause hooks.
public sealed class ThrottledSyncService : Service
{
public override bool CanPause => true;
protected override void OnStart() { /* start work */ }
protected override void OnStop() { /* stop work */ }
protected override void OnPause() { /* stop processing new items */ }
protected override void OnResume() { /* resume processing */ }
}
Asynchronous service
When the lifecycle itself involves asynchronous operations — for example, flushing a queue before stopping — inherit from AsyncService instead. The hooks receive a CancellationToken that callers can use to abort a long-running transition.
public sealed class BackgroundProcessor : AsyncService
{
protected override async Task OnStartAsync(CancellationToken cancellationToken)
{
await InitializeAsync(cancellationToken);
}
protected override async Task OnStopAsync(CancellationToken cancellationToken)
{
await FlushAsync(cancellationToken);
}
}
var worker = new BackgroundProcessor();
await worker.StartAsync(cancellationToken);
await worker.StopAsync(cancellationToken);
Service with disposal
When the service owns resources that must be released, choose a base class that also implements IDisposable or IAsyncDisposable. Override the resource cleanup methods to release what the service owns. Disposing does not stop the service: stop a running service before disposing it, or do so from your cleanup method.
public sealed class ResourceService : DisposableService
{
private readonly Stream _stream = File.OpenRead("data.bin");
protected override void OnStart() { /* ... */ }
protected override void OnStop() { /* ... */ }
protected override void DisposeManagedResources() => _stream.Dispose();
}
Available base classes
| Base class | Lifecycle | Disposal |
|---|---|---|
Service |
Sync | — |
DisposableService |
Sync | IDisposable |
AsyncDisposableService |
Sync | IDisposable + IAsyncDisposable |
AsyncService |
Async | — |
DisposableAsyncService |
Async | IDisposable |
AsyncDisposableAsyncService |
Async | IDisposable + IAsyncDisposable |
The interfaces that these classes implement live in Josupeit.Practices.Services.Abstractions, which is pulled in automatically as a transitive dependency.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. 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 was computed. 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. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 is compatible. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Josupeit.Practices.Services.Abstractions (>= 1.0.0 && < 2.0.0)
-
.NETStandard 2.1
- Josupeit.Practices.Services.Abstractions (>= 1.0.0 && < 2.0.0)
-
net8.0
- Josupeit.Practices.Services.Abstractions (>= 1.0.0 && < 2.0.0)
NuGet packages (2)
Showing the top 2 NuGet packages that depend on Josupeit.Practices.Services:
| Package | Downloads |
|---|---|
|
Josupeit.Practices.Schedulers.Core
Abstract base classes for building custom IScheduler implementations. Reduces the full IScheduler API to a single Enqueue or EnqueueAsync override with all sync/async bridging handled. |
|
|
Josupeit.Practices.Schedulers.Channel
Channel-backed IScheduler with FIFO ordering, configurable parallelism, and pause/resume support. Built on System.Threading.Channels. |
GitHub repositories
This package is not used by any popular GitHub repositories.
- Adds llms.txt and llms-full.txt for every package, a root index, and context7.json
- Corrects the package READMEs against the verified behavior
- Tracks and nullable-annotates the generated Services members
- Generates T4 output per target framework, packs the llms files, and binds dependency ranges to the referenced version
- Adds PublicApiAnalyzers with shipped API baseline
- Fixes all diagnostics from the new analyzers
- Regenerates lock files for new analyzer packages
- Adds analyzers, .editorconfig, and banned API enforcement