Skip to main content
ClaudeWave
Skill429 repo starsupdated 9d ago

worker-services

The worker-services Claude Code skill provides guidance and patterns for building long-running .NET background services using BackgroundService, Generic Host, graceful shutdown mechanisms, configuration, and logging. Use this skill when constructing scheduled workers, adding hosted services to applications, extracting services into worker processes, or reviewing cancellation handling and health check implementations in .NET background service architectures.

Install in Claude Code
Copy
git clone --depth 1 https://github.com/managedcode/dotnet-skills /tmp/worker-services && cp -r /tmp/worker-services/catalog/Frameworks/Worker-Services/skills/worker-services ~/.claude/skills/worker-services
Then start a new Claude Code session; the skill loads automatically.

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](https://learn.microsoft.com/en-us/dotnet/core/extensions/workers)
- [Background tasks with hosted services in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-10.0)
- [Create Windows Service using BackgroundService](https://learn.microsoft.com/en-us/dotnet/core/extensions/windows-service)
- [App health checks in .NET](https://learn.microsoft.com/en-us/dotnet/core/diagnostics/diagnostic-health-checks)
- [Health checks in ASP.NET Core](https://learn.microsoft.com/en-us/aspnet/core/host-and-deploy/health-checks?view=aspnetcore-10.0)

### References

- [patterns.md](references/patterns.md) - BackgroundService patterns, graceful shutdown, and health check implementations
- [anti-patterns.md](references/anti-patterns.md) - Common worker service mistakes and how to avoid them

## Workflow

1. **Use BackgroundService as your base class:**
   - Provides standard `StartAsync`/`StopAsync` handling
   - Focus on implementing `ExecuteAsync` only
   - Proper cancellation token management built-in

2. **Handle scoped dependencies correctly:**
   - Create service scopes for scoped services
   - No scope is created by default in hosted services

3. **Implement graceful shutdown:**
   - Propagate cancellation tokens throughout
   - Complete work promptly when token fires
   - Avoid ungraceful shutdown at timeout

4. **Keep execution loop thin:**
   - Move business logic to testable services
   - Handle exceptions to prevent service crashes
   - Use `PeriodicTimer` for scheduled work

5. **Add observability:**
   - Use health checks for readiness/liveness
   - Expose metrics and structured logging
   - Consider distributed locks for multi-instance

## Basic BackgroundService Pattern

### Simple Worker
```csharp
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)
```csharp
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
```csharp
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
```csharp
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)
    {
aspnet-coreSkill

Build, debug, modernize, or review ASP.NET Core applications with correct hosting, middleware, security, configuration, logging, and deployment patterns on current .NET. USE FOR: working on ASP.NET Core apps, services, or middleware; changing auth, routing, configuration, hosting, or deployment behavior; deciding between ASP.NET Core sub-stacks. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

aspireSkill

Build, upgrade, and operate .NET Aspire 13.3.x application hosts with current CLI, AppHost, ServiceDefaults, integrations, dashboard, testing, and Azure deployment patterns for distributed apps. USE FOR: Aspire.AppHost.Sdk, Aspire.Hosting.*, DistributedApplication.CreateBuilder, WithReference, WaitFor, AddProject, AddRedis, AddPostgres, aspire run, aspire init, aspire. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

azure-functionsSkill

Build, review, or migrate Azure Functions in .NET with correct execution model, isolated worker setup, bindings, DI, and Durable Functions patterns. USE FOR: working on Azure Functions in .NET; migrating from the in-process model to the isolated worker model; adding Durable Functions, bindings, or host configuration. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

blazorSkill

Build and review Blazor applications across server, WebAssembly, web app, and hybrid scenarios with correct component design, state flow, rendering, and hosting choices. USE FOR: building interactive web UIs with C# instead of JavaScript; choosing between Server, WebAssembly, or Auto render modes; designing component hierarchies and state. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

entity-framework6Skill

Maintain or migrate EF6-based applications with realistic guidance on what to keep, what to modernize, and when EF Core is or is not the right next step. USE FOR: EF6 codebases; runtime versus ORM migration decisions; EDMX, code-first, ObjectContext, and legacy data-access review. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

entity-framework-coreSkill

Design, tune, or review EF Core data access with proper modeling, migrations, query translation, performance, and lifetime management for modern .NET applications. USE FOR: DbContext, migrations, model configuration, EF queries, tracking, loading, performance, transactions, and EF6 migration decisions. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

mauiSkill

Build, review, or migrate .NET MAUI applications across Android, iOS, macOS, and Windows with correct cross-platform UI, platform integration, and native packaging assumptions. USE FOR: working on cross-platform mobile or desktop UI in .NET MAUI; integrating device capabilities, navigation, or platform-specific code; migrating Xamarin.Forms or aligning. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.

mlnetSkill

Use ML.NET to train, evaluate, or integrate machine-learning models into .NET applications with realistic data preparation, inference, and deployment expectations. USE FOR: ML.NET integration; local model training or retraining; inference pipelines, model loading, evaluation, and deployment review. DO NOT USE FOR: unrelated stacks; generic tasks that do not need this specific guidance. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.