---
description: Create BackgroundService implementations for scheduled or polling tasks (project)
---
# Add Background Service Skill
Create `BackgroundService` implementations for scheduled or polling tasks in NovaTune.
## Project Context
- Service location: `src/NovaTuneApp/NovaTuneApp.ApiService/Infrastructure/` or worker projects
- Base class: `Microsoft.Extensions.Hosting.BackgroundService`
- Pattern: Polling loop with configurable interval
## Steps
### 1. Create Options Class
Location: `src/NovaTuneApp/NovaTuneApp.ApiService/Configuration/{ServiceName}Options.cs`
```csharp
namespace NovaTuneApp.ApiService.Configuration;
public class PhysicalDeletionOptions
{
public const string SectionName = "PhysicalDeletion";
///
/// Interval between polling cycles.
/// Default: 5 minutes.
///
public TimeSpan PollingInterval { get; set; } = TimeSpan.FromMinutes(5);
///
/// Maximum items to process per cycle.
/// Default: 50.
///
public int BatchSize { get; set; } = 50;
///
/// Whether the service is enabled.
/// Default: true.
///
public bool Enabled { get; set; } = true;
}
```
### 2. Create Background Service
Location: `src/NovaTuneApp/NovaTuneApp.ApiService/Infrastructure/Services/{ServiceName}Service.cs`
```csharp
using Microsoft.Extensions.Options;
namespace NovaTuneApp.ApiService.Infrastructure.Services;
///
/// Background service that processes physical deletions for soft-deleted tracks.
///
public class PhysicalDeletionService : BackgroundService
{
private readonly IServiceProvider _serviceProvider;
private readonly IOptions _options;
private readonly ILogger _logger;
public PhysicalDeletionService(
IServiceProvider serviceProvider,
IOptions options,
ILogger logger)
{
_serviceProvider = serviceProvider;
_options = options;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
if (!_options.Value.Enabled)
{
_logger.LogInformation("Physical deletion service is disabled");
return;
}
_logger.LogInformation(
"Physical deletion service starting with {Interval} interval",
_options.Value.PollingInterval);
while (!stoppingToken.IsCancellationRequested)
{
try
{
await ProcessBatchAsync(stoppingToken);
}
catch (OperationCanceledException) when (stoppingToken.IsCancellationRequested)
{
break;
}
catch (Exception ex)
{
_logger.LogError(ex, "Error processing physical deletions");
}
await Task.Delay(_options.Value.PollingInterval, stoppingToken);
}
_logger.LogInformation("Physical deletion service stopped");
}
private async Task ProcessBatchAsync(CancellationToken ct)
{
using var scope = _serviceProvider.CreateScope();
var session = scope.ServiceProvider.GetRequiredService();
var storageService = scope.ServiceProvider.GetRequiredService();
var itemsToProcess = await session
.Query