skills/csharp-di/SKILL.md
.NET 相依性注入進階規範:Generic Host、Worker Service、Keyed Services 與 Decorator 模式。
npx skillsauth add CloudyWing/ai-dotfiles csharp-diInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
當使用者撰寫涉及 DI 容器進階配置的程式碼(如 Worker Service、多實作切換、Decorator 模式)時,請自動套用以下規範。基礎 DI Lifetime 規範參閱 csharp-aspnetcore skill。
Host.CreateDefaultBuilder() 或 Host.CreateApplicationBuilder() 建立宿主,統一 DI、Configuration、Logging 基礎設施。ServiceCollection 再 BuildServiceProvider(),除非是單元測試場景。// ✅ 正確
HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
builder.Services.AddHostedService<MyWorker>();
IHost host = builder.Build();
await host.RunAsync().ConfigureAwait(false);
// ❌ 錯誤:手動建立容器
ServiceCollection services = new();
services.AddSingleton<IMyService, MyService>();
ServiceProvider provider = services.BuildServiceProvider();
BackgroundService 並覆寫 ExecuteAsync()。ExecuteAsync() 內的迴圈必須檢查 CancellationToken,確保 Graceful Shutdown。IServiceScopeFactory 在迴圈內建立 Scope:public class OrderProcessorWorker(
IServiceScopeFactory scopeFactory,
ILogger<OrderProcessorWorker> logger
) : BackgroundService {
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
while (!stoppingToken.IsCancellationRequested) {
await using AsyncServiceScope scope = scopeFactory.CreateAsyncScope();
IOrderService orderService = scope.ServiceProvider
.GetRequiredService<IOrderService>();
await orderService.ProcessPendingOrdersAsync(stoppingToken)
.ConfigureAwait(false);
await Task.Delay(TimeSpan.FromSeconds(30), stoppingToken)
.ConfigureAwait(false);
}
}
}
BackgroundService)。// ✅ 正確
builder.Services.AddScoped<IOrderService, OrderService>();
// ❌ 錯誤
builder.Services.AddScoped<OrderService>();
builder.Services.AddKeyedScoped<INotificationService, EmailNotificationService>("email");
builder.Services.AddKeyedScoped<INotificationService, SmsNotificationService>("sms");
// 消費端
public class OrderController(
[FromKeyedServices("email")] INotificationService emailNotifier
) { }
IEnumerable<T> 接收:builder.Services.AddScoped<IValidator, NameValidator>();
builder.Services.AddScoped<IValidator, EmailValidator>();
// 消費端:注入所有 IValidator 實作
public class ValidationService(IEnumerable<IValidator> validators) { }
builder.Services.AddScoped<IProductRepository, ProductRepository>();
builder.Services.Decorate<IProductRepository, CachedProductRepository>();
builder.Services.AddScoped<ProductRepository>();
builder.Services.AddScoped<IProductRepository>(sp => {
ProductRepository inner = sp.GetRequiredService<ProductRepository>();
return new CachedProductRepository(inner, sp.GetRequiredService<IMemoryCache>());
});
ValidateScopes 與 ValidateOnBuild,及早發現 Captive Dependency 與遺漏註冊:HostApplicationBuilder builder = Host.CreateApplicationBuilder(args);
if (builder.Environment.IsDevelopment()) {
builder.Services.BuildServiceProvider(new ServiceProviderOptions {
ValidateScopes = true,
ValidateOnBuild = true
});
}
IServiceProvider.GetService<T>()。服務應透過建構函式注入,而非在方法內自行解析。唯一允許的例外是工廠方法與 BackgroundService 內的 Scope 建立。IServiceProvider 存放於靜態欄位供全域存取(如 ServiceLocator.Instance)。tools
產生或補齊 .gitattributes,統一行尾處理、二進位識別與 lock files 標記,保留既有自訂偏好。
development
產生或補齊前端 Lint 設定(Prettier + ESLint Flat Config),統一格式化與程式碼品質規則,保留既有自訂偏好。
testing
依據事實校閱報告修改技術文件:以事實層為不可違反的約束,由改檔者負責表達層的措辭與行文連貫。Use when the user asks to apply fact-check results to a document, or to edit a document based on a previously produced fact-check-report.md.
data-ai
多份資料檔整合流程。當需要將兩份以上的資料檔(如 JSON、CSV)合併、補齊闕漏欄位或去重成單一檔案時使用。以 dry-run、筆數核對與抽樣比對降低整合錯誤。