Postpartum-genushyacinthus29/dotnet-skills/skills/dotnet-worker-services/SKILL.md
dotnet-worker-services
Build long-running .NET background services with `BackgroundService`, Generic Host, graceful shutdown, configuration, logging, and deployment patterns suited to workers and daemons.
- Source repository stars
- 9
- Declared platforms
- 0
- Static risk flags
- 0
- Last source update
- 2026-08-23
- Source checked
- 2026-08-25
Decision brief
What it does: where it fits
Build long-running . NET background services with `BackgroundService`, Generic Host, graceful shutdown, configuration, logging, and deployment patterns suited to workers and daemons.
Not for
- Tasks that require unconfirmed production actions or broad system permissions.
- Environments where the pinned source and install steps cannot be inspected.
Compatibility matrix
Platform support, with evidence labels
| Platform | Status | Evidence | What to check |
|---|---|---|---|
| Codex | Not declared | No explicit evidence | Portability before use |
| Claude Code | Not declared | No explicit evidence | Portability before use |
| Cursor | Not declared | No explicit evidence | Portability before use |
| Gemini CLI | Not declared | No explicit evidence | Portability before use |
Installation
Inspect first. Install second.
The source command is displayed only when detected. A safe inspection prompt is always available so your agent can explain every action before execution.
npx skills add https://github.com/Postpartum-genushyacinthus29/dotnet-skills --skill "skills/dotnet-worker-services"Inspect the Agent Skill "dotnet-worker-services" from https://github.com/Postpartum-genushyacinthus29/dotnet-skills/blob/f9c1a213bc25d95641adc3a59f8048cb5656741c/skills/dotnet-worker-services/SKILL.md at commit f9c1a213bc25d95641adc3a59f8048cb5656741c. List every install step, command, network request, credential, file read/write, external action, and rollback step. Explain whether it fits my task. Do not install or execute anything until I approve.
Workflow
What the source asks the agent to do
- 01
Workflow
1. Use BackgroundService as your base class: - Provides standard StartAsync/StopAsync handling - Focus on implementing ExecuteAsync only - Proper cancellation token management built-in
Use BackgroundService as your base class:Provides standard StartAsync/StopAsync handlingFocus on implementing ExecuteAsync only - 02
Trigger On
building long-running background services or scheduled workers
building long-running background services or scheduled workersadding hosted services to an app or extracting them into a worker processreviewing graceful shutdown, cancellation, queue processing, or health behavior - 03
Documentation
Worker Services in .NET
Worker Services in .NETBackground tasks with hosted services in ASP.NET CoreCreate Windows Service using BackgroundService - 04
Basic BackgroundService Pattern
Review the “Basic BackgroundService Pattern” section in the pinned source before continuing.
Review and apply the “Basic BackgroundService Pattern” source section. - 05
Simple Worker
Review the “Simple Worker” section in the pinned source before continuing.
Review and apply the “Simple Worker” source section.
Permission review
Static risk signals and limitations
No configured static risk pattern was detected
This is not proof of safety. Runtime behavior, indirect dependencies, and hidden external systems are outside the static scan.
Evidence record
Why each signal appears
| Signal | Value | Evidence type | Meaning |
|---|---|---|---|
| Quality score | 100/100 | Computed | Documentation, specificity, maintenance, and trust rules |
| Repository stars | 9 | Source | Repository attention, not individual Skill quality |
| Compatibility | 0 platforms | Source | Declared in the catalog source record |
| Usage guide | automated source guide | Editorial | Generated or reviewed according to the visible evidence level |
Pinned source
Provenance and original SKILL.md
- Repository
- Postpartum-genushyacinthus29/dotnet-skills
- Skill path
- skills/dotnet-worker-services/SKILL.md
- Commit
- f9c1a213bc25d95641adc3a59f8048cb5656741c
- License
- MIT
- Collected
- 2026-08-25
- Default branch
- main
View the original SKILL.md
.NET Worker Services
Trigger On
- building long-running background services or scheduled workers
- adding hosted services to an app or extracting them into a worker process
- reviewing graceful shutdown, cancellation, queue processing, or health behavior
Documentation
- Worker Services in .NET
- Background tasks with hosted services in ASP.NET Core
- Create Windows Service using BackgroundService
- App health checks in .NET
- Health checks in ASP.NET Core
References
- patterns.md - BackgroundService patterns, graceful shutdown, and health check implementations
- anti-patterns.md - Common worker service mistakes and how to avoid them
Workflow
-
Use BackgroundService as your base class:
- Provides standard
StartAsync/StopAsynchandling - Focus on implementing
ExecuteAsynconly - Proper cancellation token management built-in
- Provides standard
-
Handle scoped dependencies correctly:
- Create service scopes for scoped services
- No scope is created by default in hosted services
-
Implement graceful shutdown:
- Propagate cancellation tokens throughout
- Complete work promptly when token fires
- Avoid ungraceful shutdown at timeout
-
Keep execution loop thin:
- Move business logic to testable services
- Handle exceptions to prevent service crashes
- Use
PeriodicTimerfor scheduled work
-
Add observability:
- Use health checks for readiness/liveness
- Expose metrics and structured logging
- Consider distributed locks for multi-instance
Basic BackgroundService Pattern
Simple Worker
public class Worker : BackgroundService
{
private readonly ILogger<Worker> _logger;
public Worker(ILogger<Worker> logger)
{
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Worker starting");
while (!stoppingToken.IsCancellationRequested)
{
try
{
_logger.LogInformation("Worker running at: {Time}", DateTimeOffset.Now);
await DoWorkAsync(stoppingToken);
await Task.Delay(TimeSpan.FromSeconds(5), stoppingToken);
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
// Graceful shutdown, not an error
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error in worker iteration");
// Continue or break based on error severity
}
}
_logger.LogInformation("Worker stopping");
}
private async Task DoWorkAsync(CancellationToken cancellationToken)
{
// Business logic here
}
}
Using PeriodicTimer (Recommended)
public class TimedWorker : BackgroundService
{
private readonly ILogger<TimedWorker> _logger;
private readonly IServiceScopeFactory _scopeFactory;
private readonly TimeSpan _period = TimeSpan.FromMinutes(1);
public TimedWorker(ILogger<TimedWorker> logger, IServiceScopeFactory scopeFactory)
{
_logger = logger;
_scopeFactory = scopeFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
using var timer = new PeriodicTimer(_period);
while (await timer.WaitForNextTickAsync(stoppingToken))
{
try
{
await using var scope = _scopeFactory.CreateAsyncScope();
var processor = scope.ServiceProvider.GetRequiredService<IDataProcessor>();
await processor.ProcessAsync(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing scheduled task");
}
}
}
}
Handling Scoped Dependencies
Correct Pattern with Scope Factory
public class ScopedWorker : BackgroundService
{
private readonly IServiceScopeFactory _scopeFactory;
private readonly ILogger<ScopedWorker> _logger;
public ScopedWorker(IServiceScopeFactory scopeFactory, ILogger<ScopedWorker> logger)
{
_scopeFactory = scopeFactory;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
// Create scope for each unit of work
await using var scope = _scopeFactory.CreateAsyncScope();
var dbContext = scope.ServiceProvider.GetRequiredService<AppDbContext>();
var service = scope.ServiceProvider.GetRequiredService<IScopedService>();
await service.ProcessAsync(dbContext, stoppingToken);
await Task.Delay(TimeSpan.FromSeconds(10), stoppingToken);
}
}
}
Queue Processing Pattern
Message Queue Worker
public class QueueWorker : BackgroundService
{
private readonly ILogger<QueueWorker> _logger;
private readonly IServiceScopeFactory _scopeFactory;
private readonly IBackgroundTaskQueue _taskQueue;
public QueueWorker(
ILogger<QueueWorker> logger,
IServiceScopeFactory scopeFactory,
IBackgroundTaskQueue taskQueue)
{
_logger = logger;
_scopeFactory = scopeFactory;
_taskQueue = taskQueue;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Queue Worker started");
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await _taskQueue.DequeueAsync(stoppingToken);
try
{
await using var scope = _scopeFactory.CreateAsyncScope();
await workItem(scope.ServiceProvider, stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing queued work item");
// Handle poison message - retry, dead-letter, etc.
}
}
}
}
// Task queue interface
public interface IBackgroundTaskQueue
{
ValueTask QueueBackgroundWorkItemAsync(
Func<IServiceProvider, CancellationToken, ValueTask> workItem);
ValueTask<Func<IServiceProvider, CancellationToken, ValueTask>> DequeueAsync(
CancellationToken cancellationToken);
}
Health Checks for Workers
Adding Health Check Endpoint
// Program.cs
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<Worker>();
// Add health checks
builder.Services.AddHealthChecks()
.AddCheck<WorkerHealthCheck>("worker_health")
.AddResourceUtilizationHealthCheck();
// Add HTTP endpoint for health checks
builder.Services.AddHealthChecksUI();
// Or use simple TCP listener for Kubernetes
builder.Services.AddSingleton<TcpHealthProbeService>();
builder.Services.AddHostedService(sp => sp.GetRequiredService<TcpHealthProbeService>());
var host = builder.Build();
host.Run();
Custom Health Check
public class WorkerHealthCheck : IHealthCheck
{
private readonly WorkerState _workerState;
public WorkerHealthCheck(WorkerState workerState)
{
_workerState = workerState;
}
public Task<HealthCheckResult> CheckHealthAsync(
HealthCheckContext context,
CancellationToken cancellationToken = default)
{
if (_workerState.LastSuccessfulRun > DateTime.UtcNow.AddMinutes(-5))
{
return Task.FromResult(HealthCheckResult.Healthy(
$"Last successful run: {_workerState.LastSuccessfulRun}"));
}
return Task.FromResult(HealthCheckResult.Unhealthy(
$"No successful run since: {_workerState.LastSuccessfulRun}"));
}
}
// Shared state
public class WorkerState
{
public DateTime LastSuccessfulRun { get; set; } = DateTime.UtcNow;
public bool IsProcessing { get; set; }
}
Graceful Shutdown Pattern
Proper Shutdown Handling
public class GracefulWorker : BackgroundService
{
private readonly ILogger<GracefulWorker> _logger;
private int _currentWorkItemId;
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
_logger.LogInformation("Worker starting");
while (!stoppingToken.IsCancellationRequested)
{
_currentWorkItemId = GetNextWorkItemId();
try
{
// Pass cancellation token to all async operations
await ProcessWorkItemAsync(_currentWorkItemId, stoppingToken);
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
_logger.LogInformation(
"Shutdown requested, stopping after work item {Id}", _currentWorkItemId);
break;
}
}
}
public override async Task StopAsync(CancellationToken cancellationToken)
{
_logger.LogInformation("Worker stopping gracefully");
await base.StopAsync(cancellationToken);
_logger.LogInformation("Worker stopped");
}
}
Windows Service Deployment
Configuring as Windows Service
// Program.cs
var builder = Host.CreateApplicationBuilder(args);
builder.Services.AddWindowsService(options =>
{
options.ServiceName = "My Worker Service";
});
builder.Services.AddHostedService<Worker>();
var host = builder.Build();
host.Run();
Project File Settings
<Project Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>
Best Practices
- Use BackgroundService as base class - Handles
StartAsync/StopAsyncboilerplate and cancellation management - Create scopes for scoped dependencies - Use
IServiceScopeFactoryto resolve scoped services like DbContext - Propagate cancellation tokens everywhere - Pass to all async methods for responsive shutdown
- Wrap work in try-catch - Unhandled exceptions stop the service completely
- Use PeriodicTimer for timed tasks - Cleaner than
Task.Delaywith proper cancellation support - Add health checks - Essential for Kubernetes liveness/readiness probes
- Avoid blocking StartAsync - Long initialization delays other hosted services
- Call base methods when overriding - Always call
await base.StartAsync()andawait base.StopAsync() - Publish as single file for Windows Service - Reduces deployment complexity and errors
- Consider scaling requirements - Separate worker projects if independent scaling is needed
Anti-Patterns to Avoid
| Anti-Pattern | Why It's Bad | Better Approach |
|---|---|---|
Ad-hoc while(true) loops | No graceful shutdown, poor lifecycle | Use BackgroundService |
| Ignoring cancellation token | Ungraceful shutdown, resource leaks | Propagate token to all async calls |
| Injecting scoped services directly | Captive dependencies, memory leaks | Use IServiceScopeFactory |
Unhandled exceptions in ExecuteAsync | Silently stops the worker | Wrap in try-catch, log, continue |
Long-running StartAsync | Blocks other services from starting | Move work to ExecuteAsync |
async void methods | Crashes process on exception | Use async Task |
| Missing health checks | No visibility into worker status | Implement IHealthCheck |
| Polling with tight loops | CPU waste, no responsiveness | Use PeriodicTimer or event-driven |
Not overriding StopAsync | Missed cleanup opportunity | Override for graceful cleanup |
| Singleton DbContext | Not thread-safe, stale data | Create scopes per operation |
Deliver
- well-behaved worker processes and hosted services
- predictable startup and shutdown behavior
- proper scoped dependency handling
- health checks for production observability
- retry and poison-message handling for queue work
Validate
- cancellation token propagated and shutdown honored
- scoped services resolved within proper scopes
- exception handling prevents service crashes
- health checks report accurate worker status
- runtime behavior visible through logs or telemetry
- no blocking calls in async context
Frequently asked questions
What to verify before installation and use
What does the dotnet-worker-services source document cover?
Build long-running . NET background services with `BackgroundService`, Generic Host, graceful shutdown, configuration, logging, and deployment patterns suited to workers and daemons.
How do I install dotnet-worker-services?
The source record exposes this install command: npx skills add https://github.com/Postpartum-genushyacinthus29/dotnet-skills --skill "skills/dotnet-worker-services". Inspect the command and pinned source before running it.
Alternatives
Compare before choosing
vasilyu1983/AI-Agents-public
research-git
Scans public GitHub repos for agent skills, dev practices, and code patterns. Use when enriching skills, setting team policy, or researching a build domain.
Postpartum-genushyacinthus29/dotnet-skills
dotnet-azure-functions
Build, review, or migrate Azure Functions in .NET with correct execution model, isolated worker setup, bindings, DI, and Durable Functions patterns.
huggingface/skills
huggingface-lora-space-builder
Build and publish a Gradio demo on Hugging Face Spaces for a user-provided LoRA. Use when someone asks to create, generate, ship, or publish a Space, demo, Gradio app, or playground for a LoRA — including LoRAs for Qwen-Image, Qwen-Image-Edit, LTX-Video, Wan, FLUX, SDXL, or other diffusion base models. Also triggers when someone describes a LoRA they trained or hosts on the Hub and wants to share it. Covers picking the right base pipeline and `diffusers` inference recipe, designing a UI tailored
open-edge-platform/edge-ai-libraries
chatqna-helm-deploy
Deploy Chat Question-and-Answer Core to Kubernetes using Helm (OpenVINO CPU, OpenVINO GPU, or Ollama), including values.yaml configuration, helm install/upgrade, deployment verification, uninstall, and translation from Docker Compose setup_env.sh variables into Helm override values. Use this skill when the user says "deploy chatqna core to kubernetes", "helm install chatqna-core", "configure values.yaml", "convert compose config to helm", or "translate setup_env.sh to chart values".