Route gh-aw workflow design/create/debug/upgrade requests to the right prompts.
日本語の概要は準備中です。原文の説明を表示しています。
Build long-running .NET background services with `BackgroundService`, Generic Host, graceful shutdown, configuration, logging, and deployment patterns suited to workers and daemons. USE FOR: background services; scheduled workers; hosted services; worker extraction; graceful shutdown, health checks, and service hosting 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.
インストール方法を見るインストールする前に、エージェントに与えられる指示の中身を確認できます。
Use BackgroundService as your base class:
StartAsync/StopAsync handlingExecuteAsync onlyHandle scoped dependencies correctly:
Implement graceful shutdown:
Keep execution loop thin:
PeriodicTimer for scheduled workAdd observability:
.NET runtime v10.0.11 is servicing. For workers, rerun cgroup-v2 memory-limit detection, cancellation, graceful shutdown, long-running thread-static and GC paths, NativeAOT/WASM startup where relevant, and Windows directory enumeration after upgrading rather than changing architecture by default.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
}
}
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");
}
}
}
}
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);
}
}
}
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);
}
// 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();
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; }
}
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");
}
}
// 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 Sdk="Microsoft.NET.Sdk.Worker">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
<RuntimeIdentifier>win-x64</RuntimeIdentifier>
<PublishSingleFile>true</PublishSingleFile>
<SelfContained>true</SelfContained>
</PropertyGroup>
</Project>
StartAsync/StopAsync boilerplate and cancellation managementIServiceScopeFactory to resolve scoped services like DbContextTask.Delay with proper cancellation supportawait base.StartAsync() and await base.StopAsync()| 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 |
まだレビューはありません。使ってみた感想をお寄せください。
概要と使いどころ
Route gh-aw workflow design/create/debug/upgrade requests to the right prompts.
日本語の概要は準備中です。原文の説明を表示しています。
Use a repo-root `.editorconfig` to configure free .NET analyzer and style rules. Use when a .NET repo needs rule severity, code-style options, section layout, or analyzer ownership made explicit. USE FOR: the repo needs a root .editorconfig; analyzer severity and style ownership are unclear; the team wants one source of truth for rule configuration. DO NOT USE FOR: choosing analyzers with no config change; formatting-only execution with no config ownership question. INVOKES: inspect the repository context, edit targeted files, and run relevant build, test, lint, or validation commands when changes are made.
日本語の概要は準備中です。原文の説明を表示しています。
Scans .NET code for ~50 performance anti-patterns across async, memory, strings, collections, LINQ, regex, serialization, and I/O with tiered severity classification. Use when analyzing .NET code for optimization opportunities, reviewing hot paths, or auditing allocation-heavy patterns.
日本語の概要は準備中です。原文の説明を表示しています。
Symbolicate the .NET runtime frames in an Android tombstone file. Extracts BuildIds and PC offsets from the native backtrace, downloads debug symbols from the Microsoft symbol server, and runs llvm-symbolizer to produce function names with source file and line numbers. USE FOR triaging a .NET MAUI or Mono Android app crash from a tombstone, resolving native backtrace frames in libmonosgen-2.0.so or libcoreclr.so to .NET runtime source code, or investigating SIGABRT, SIGSEGV, or other native signals originating from the .NET runtime on Android. DO NOT USE FOR pure Java/Kotlin crashes, managed .NET exceptions that are already captured in logcat, or iOS crash logs. INVOKES Symbolicate-Tombstone.ps1 script, llvm-symbolizer, Microsoft symbol server.
日本語の概要は準備中です。原文の説明を表示しています。
Symbolicate .NET runtime frames in Apple platform .ips crash logs (iOS, tvOS, Mac Catalyst, macOS). Extracts UUIDs and addresses from the native backtrace, locates dSYM debug symbols, and runs atos to produce function names with source file and line numbers. Automatically downloads .dwarf symbols from the Microsoft symbol server using Mach-O UUIDs. USE FOR triaging a .NET MAUI or Mono app crash from an .ips file on any Apple platform, resolving native backtrace frames in libcoreclr or libmonosgen-2.0 to .NET runtime source code, retrieving .ips crash logs from a connected iOS device or iPhone, or investigating EXC_CRASH, EXC_BAD_ACCESS, SIGABRT, or SIGSEGV originating from the .NET runtime. DO NOT USE FOR pure Swift/Objective-C crashes with no .NET components, or Android tombstone files. INVOKES Symbolicate-Crash.ps1 script, atos, dwarfdump, idevicecrashreport.
日本語の概要は準備中です。原文の説明を表示しています。
Design or review .NET solution architecture across modular monoliths, clean architecture, vertical slices, microservices, DDD, CQRS, and cloud-native boundaries without over-engineering. USE FOR: .NET architecture choices; layer and domain boundary review; service decomposition; clean architecture, vertical slice, DDD, CQRS, and modular monolith 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.
日本語の概要は準備中です。原文の説明を表示しています。