ef-corelisted
Install: claude install-skill CloudyWing/ai-dotfiles
# Entity Framework Core 開發規範
## DbContext Lifetime(Crucial)
- DbContext **必須**註冊為 `Scoped`(`AddDbContext<T>` 預設行為)。
- **禁止**將 DbContext 注入至 Singleton 服務,會導致 Captive Dependency 與並行存取問題。
- 若需在 Singleton 或背景服務中使用,必須注入 `IDbContextFactory<T>` 並在使用處建立短生命週期實例。
```csharp
// ✅ 正確:背景服務使用 IDbContextFactory
public class MyBackgroundService : BackgroundService {
private readonly IDbContextFactory<AppDbContext> contextFactory;
public MyBackgroundService(IDbContextFactory<AppDbContext> contextFactory) {
this.contextFactory = contextFactory;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken) {
await using AppDbContext db = await contextFactory.CreateDbContextAsync(stoppingToken)
.ConfigureAwait(false);
// ...
}
}
// ❌ 錯誤:直接注入 DbContext 至 Singleton
public class MyBackgroundService : BackgroundService {
public MyBackgroundService(AppDbContext db) { /* ... */ }
}
```
## 查詢效能(Crucial)
### N+1 問題防範
- 迴圈中對 Navigation Property 的逐筆存取為典型 N+1 模式,必須使用 `Include()` / `ThenInclude()` 預先載入,或改用投影(`Select`)一次取回所需欄位。
- 禁止在迴圈中呼叫 `SaveChanges()` 或 `SaveChangesAsync()`,應批次處理後統一儲存。
### AsNoTracking
- **唯讀查詢**(不需要後續更新的資料)必須加上 `.AsNoTracking()`,降低記憶體與效能開銷。
- 若整個 DbContext 的查詢皆為唯讀場景,可在註冊時設定 `UseQueryTrackingBehavior(QueryTrackingBehavior.NoTracking)`。
```csharp
// ✅ 正確:唯讀查詢
IReadOnlyList<OrderDto> orders = await db.Orders
.AsNoTracking()
.Where(o => o.Status == OrderStatus.Completed)
.Select(o => new OrderD