Source profileQuality 100/100

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.

Best for

    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

    PlatformStatusEvidenceWhat to check
    CodexNot declaredNo explicit evidencePortability before use
    Claude CodeNot declaredNo explicit evidencePortability before use
    CursorNot declaredNo explicit evidencePortability before use
    Gemini CLINot declaredNo explicit evidencePortability before use
    Open the compatibility checker

    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.

    Source-detected install commandSource
    npx skills add https://github.com/Postpartum-genushyacinthus29/dotnet-skills --skill "skills/dotnet-worker-services"
    Safe inspection promptEditorial

    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

    1. 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
    2. 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
    3. 03

      Documentation

      Worker Services in .NET

      Worker Services in .NETBackground tasks with hosted services in ASP.NET CoreCreate Windows Service using BackgroundService
    4. 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.
    5. 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

    EvidenceSourceComputedTestedEditorial
    SignalValueEvidence typeMeaning
    Quality score100/100ComputedDocumentation, specificity, maintenance, and trust rules
    Repository stars9SourceRepository attention, not individual Skill quality
    Compatibility0 platformsSourceDeclared in the catalog source record
    Usage guideautomated source guideEditorialGenerated 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

    References

    • patterns.md - BackgroundService patterns, graceful shutdown, and health check implementations
    • 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

    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

    1. Use BackgroundService as base class - Handles StartAsync/StopAsync boilerplate and cancellation management
    2. Create scopes for scoped dependencies - Use IServiceScopeFactory to resolve scoped services like DbContext
    3. Propagate cancellation tokens everywhere - Pass to all async methods for responsive shutdown
    4. Wrap work in try-catch - Unhandled exceptions stop the service completely
    5. Use PeriodicTimer for timed tasks - Cleaner than Task.Delay with proper cancellation support
    6. Add health checks - Essential for Kubernetes liveness/readiness probes
    7. Avoid blocking StartAsync - Long initialization delays other hosted services
    8. Call base methods when overriding - Always call await base.StartAsync() and await base.StopAsync()
    9. Publish as single file for Windows Service - Reduces deployment complexity and errors
    10. Consider scaling requirements - Separate worker projects if independent scaling is needed

    Anti-Patterns to Avoid

    Anti-PatternWhy It's BadBetter Approach
    Ad-hoc while(true) loopsNo graceful shutdown, poor lifecycleUse BackgroundService
    Ignoring cancellation tokenUngraceful shutdown, resource leaksPropagate token to all async calls
    Injecting scoped services directlyCaptive dependencies, memory leaksUse IServiceScopeFactory
    Unhandled exceptions in ExecuteAsyncSilently stops the workerWrap in try-catch, log, continue
    Long-running StartAsyncBlocks other services from startingMove work to ExecuteAsync
    async void methodsCrashes process on exceptionUse async Task
    Missing health checksNo visibility into worker statusImplement IHealthCheck
    Polling with tight loopsCPU waste, no responsivenessUse PeriodicTimer or event-driven
    Not overriding StopAsyncMissed cleanup opportunityOverride for graceful cleanup
    Singleton DbContextNot thread-safe, stale dataCreate 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

    Computed 9880

    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.

    Computed 979

    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.

    Computed 9610,956

    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

    Computed 96156

    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".