用 Codex 或 Claude 帮你安装 复制这段 Prompt,粘贴到 Codex、Claude 或其他助手里,让它检查 Skill 页面并帮你完成安装。
直接命令不会经过审查 Prompt;运行前请先检查来源。
npx skills add https://github.com/rudironsoni/Synaxis --skill dotnet-background-services命令会保持在同一行。复制前请横向滚动并检查完整内容。
想先保存到本地?可下载 SkillsMP 当前能够提供的文件。
Routes .NET/C# work to domain skills. Loads coding-standards for code paths.
基于 SOC 职业分类
正在显示 SKILL.md
| name | dotnet-background-services |
| category | fundamentals |
| subcategory | coding-standards |
| description | Implements background work. BackgroundService, IHostedService, lifecycle, graceful shutdown. |
| license | MIT |
| targets | ["*"] |
| tags | ["architecture","dotnet","skill"] |
| version | 0.0.1 |
| author | dotnet-agent-harness |
| invocable | true |
| claudecode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
| codexcli | {"short-description":".NET skill guidance for architecture tasks"} |
| opencode | {"allowed-tools":["Read","Grep","Glob","Bash","Write","Edit"]} |
| copilot | {} |
| geminicli | {} |
| antigravity | {} |
Patterns for long-running background work in .NET applications. Covers BackgroundService, IHostedService, hosted
service lifecycle, and graceful shutdown handling.
Cross-references: [skill:dotnet-csharp-async-patterns] for async patterns in background workers, [skill:dotnet-csharp-dependency-injection] for hosted service registration and scope management, [skill:dotnet-channels] for Channel patterns used in background work queues.
| Feature | BackgroundService | IHostedService |
|---|---|---|
| Purpose | Long-running loop or continuous work | Startup/shutdown hooks |
| Methods | Override ExecuteAsync | Implement StartAsync + StopAsync |
| Lifetime | Runs until cancellation or host shutdown | StartAsync runs at startup, StopAsync at shutdown |
| Use when | Polling queues, processing streams, periodic jobs | Database migrations, cache warming, resource cleanup |
public sealed class OrderProcessorWorker(
IServiceScopeFactory scopeFactory,
ILogger<OrderProcessorWorker> logger) : BackgroundService
{
{
logger.LogInformation();
(!stoppingToken.IsCancellationRequested)
{
{
scope = scopeFactory.CreateScope();
processor = scope.ServiceProvider
.GetRequiredService<IOrderProcessor>();
processed = processor.ProcessPendingAsync(stoppingToken);
(processed == )
{
Task.Delay(TimeSpan.FromSeconds(), stoppingToken);
}
}
(OperationCanceledException) (stoppingToken.IsCancellationRequested)
{
;
}
(Exception ex)
{
logger.LogError(ex, );
Task.Delay(TimeSpan.FromSeconds(), stoppingToken);
}
}
logger.LogInformation();
}
}
builder.Services.AddHostedService<OrderProcessorWorker>();
```text
**Always create scopes** -- `BackgroundService` registered a singleton. Inject `IServiceScopeFactory`,
services directly.
**Always handle exceptions** -- , unhandled exceptions `ExecuteAsync`
{
{
logger.LogInformation();
scope = scopeFactory.CreateScope();
cache = scope.ServiceProvider.GetRequiredService<IProductCache>();
cache.WarmAsync(cancellationToken);
logger.LogInformation();
}
=> Task.CompletedTask;
}
```text
```
{
IConnection? _connection;
{
logger.LogInformation();
_connection = CreateConnectionAsync(cancellationToken);
}
{
logger.LogInformation();
(_connection )
{
_connection.CloseAsync(cancellationToken);
_connection = ;
}
}
{
NotImplementedException();
}
}
```text
---
Understanding the startup shutdown sequence critical correct behavior.
`IHostedService.StartAsync` called each registered service ** registration order**
`BackgroundService.ExecuteAsync` called after `StartAsync` completes (it runs concurrently -- the host does
wait it to finish)
The host ready to serve requests after all `StartAsync` calls complete
**Important:** `ExecuteAsync` must block before yielding to the caller. The first `` `ExecuteAsync`
control returns to the host. If you have synchronous setup before the first ``, keep it move it to
`StartAsync` via an .
```csharp
:
{
{
InitializeAsync(cancellationToken);
.StartAsync(cancellationToken);
}
{
(!stoppingToken.IsCancellationRequested)
{
DoWorkAsync(stoppingToken);
}
}
=> Task.CompletedTask;
=> Task.CompletedTask;
}
```text
`IHostApplicationLifetime.ApplicationStopping` triggered
The host calls `StopAsync` each hosted service ** reverse registration order**
For `BackgroundService`, the stopping token cancelled, then `StopAsync` waits `ExecuteAsync` to complete
`IHostApplicationLifetime.ApplicationStopped` triggered
---
See [skill:dotnet-channels] comprehensive `Channel<T>` guidance including bounded/unbounded options,
`BoundedChannelFullMode`, backpressure strategies, `itemDropped` callbacks, multiple consumers, performance tuning,
drain patterns.
The most common integration a channel-backed background task queue consumed a `BackgroundService`:
```csharp
{
Channel<Func<IServiceProvider, CancellationToken, Task>> _queue
= Channel.CreateBounded<Func<IServiceProvider, CancellationToken, Task>>(
BoundedChannelOptions() { FullMode = BoundedChannelFullMode.Wait });
ChannelWriter<Func<IServiceProvider, CancellationToken, Task>> Writer => _queue.Writer;
ChannelReader<Func<IServiceProvider, CancellationToken, Task>> Reader => _queue.Reader;
}
{
{
( queue.Reader.WaitToReadAsync(stoppingToken))
{
(queue.Reader.TryRead( workItem))
{
{
scope = scopeFactory.CreateScope();
workItem(scope.ServiceProvider, stoppingToken);
}
(Exception ex)
{
logger.LogError(ex, );
}
}
}
}
}
builder.Services.AddSingleton<BackgroundTaskQueue>();
builder.Services.AddHostedService<QueueProcessorWorker>();
```text
---
By , the host waits seconds services to stop. Configure -running operations:
```csharp
builder.Services.Configure<HostOptions>(options =>
{
options.ShutdownTimeout = TimeSpan.FromSeconds();
});
```text
```
{
{
lifetime.ApplicationStarted.Register(() =>
logger.LogInformation());
lifetime.ApplicationStopping.Register(() =>
logger.LogInformation());
lifetime.ApplicationStopped.Register(() =>
logger.LogInformation());
Task.CompletedTask;
}
=> Task.CompletedTask;
}
```text
---
Use `PeriodicTimer` instead of `Task.Delay` more accurate periodic execution:
```
{
{
timer = PeriodicTimer(TimeSpan.FromMinutes());
( timer.WaitForNextTickAsync(stoppingToken))
{
{
scope = scopeFactory.CreateScope();
reporter = scope.ServiceProvider
.GetRequiredService<IHealthReporter>();
reporter.ReportAsync(stoppingToken);
}
(Exception ex)
{
logger.LogError(ex, );
}
}
}
}
```text
---
**Do inject services BackgroundService constructors** -- they are singletons. Always use
`IServiceScopeFactory`.
**Do use `Task.Run` background work** -- use `BackgroundService` proper lifecycle management graceful
shutdown.
**Do swallow `OperationCanceledException`** -- it propagate re-check the stopping token. Swallowing it
prevents graceful shutdown.
**Do use `Thread.Sleep`** -- use ` Task.Delay(duration, stoppingToken)` `PeriodicTimer`.
**Do forget to register** -- `AddHostedService<T>()` ; merely implementing the .
---
## ( )
** :** :
1. ** **: ``
2. ** **: ``
3. ** **: ``
4. ** **: ``
** :**
- ✅ ** **: , , ,
- ✅ ** /**: , ,
- ✅ ****: ,
** :**
```
# :
: //
: " "
# :
: "/"
: "//"
```
##
- [ ](:
- [](:
- [ ](:
- [ ](:
- [](: