# Gotchas — SOLUTION_ERP > Các bẫy, pitfall đã gặp + cách xử lý. Đọc trước khi debug tương tự để không mất thời gian. ## Tech stack constraints (.NET 10 + TS 6 + Vite 8) ### 1. MediatR 14.x không tương thích → pin 12.4.1 **Triệu chứng:** `Unable to resolve service for type 'MediatR.IMediator'` — `AddMediatR` vẫn chạy nhưng không register IMediator. **Nguyên nhân:** MediatR v14 (late 2025) refactored, extension methods khác. **Fix:** Downgrade ``. Khi đó `RequestHandlerDelegate` là delegate không tham số (v14 có thêm CancellationToken param). ### 2. Swashbuckle 10.x + Microsoft.OpenApi 2.x breaking change **Triệu chứng:** Build fail `The type or namespace 'Models' does not exist in 'Microsoft.OpenApi'`. Swagger endpoint 404. **Nguyên nhân:** `.NET 10` template auto-cài `Microsoft.AspNetCore.OpenApi 10` → pull `Microsoft.OpenApi 2.0` → namespace `Microsoft.OpenApi.Models` đã bị remove. **Fix:** - Remove `Microsoft.AspNetCore.OpenApi` khỏi Api - Downgrade Swashbuckle về `6.9.0` (compatible với OpenApi 1.x) ### 3. TypeScript 6 `erasableSyntaxOnly` cấm `enum` **Triệu chứng:** `TS1294: This syntax is not allowed when 'erasableSyntaxOnly' is enabled.` khi dùng `enum`. **Nguyên nhân:** Vite 8 scaffold bật `erasableSyntaxOnly: true` — enum sinh runtime code nên bị cấm. **Fix:** Dùng `const + as const + typeof[keyof]` pattern: ```ts // ❌ Không được export enum SupplierType { NhaCungCap = 1 } // ✅ OK export const SupplierType = { NhaCungCap: 1, NhaThauPhu: 2 } as const export type SupplierType = typeof SupplierType[keyof typeof SupplierType] ``` ### 4. TypeScript 6 deprecate `baseUrl` **Triệu chứng:** `TS5101: Option 'baseUrl' is deprecated`. **Fix:** Bỏ `baseUrl` trong `tsconfig.app.json`, chỉ giữ `paths`. Paths resolve relative to tsconfig location. ### 5. Node 22 local nhưng CI phải pin 20 **Bài học NamGroup:** CI build fail trên Node latest, phải downgrade. Dev local dùng Node 22 thoải mái. **Fix:** - `package.json` engines: `">=20"` (min only, không upper bound) - `.nvmrc` = `20` (CI dùng) - GitHub/Gitea Actions: `actions/setup-node@v4` với `node-version-file: '.nvmrc'` hoặc hardcode `'20.x'` ## EF Core 10 ### 6. Expression tree không support switch expression **Triệu chứng:** `CS8514: An expression tree may not contain a switch expression` khi viết `.AnyAsync(p => action switch { ... })`. **Fix:** Tách switch ra ngoài, mỗi case gọi query riêng: ```csharp // ❌ var hasPermission = await query.AnyAsync(p => action switch { "Read" => p.CanRead, ... }); // ✅ var hasPermission = action switch { "Read" => await query.AnyAsync(p => p.CanRead), "Create" => await query.AnyAsync(p => p.CanCreate), ... }; ``` ### 7. Design-time DbContext resolve fail **Triệu chứng:** `dotnet ef migrations add` → `Unable to resolve service for type 'DbContextOptions'`. **Fix:** Tạo `IDesignTimeDbContextFactory` trong Infrastructure — EF CLI sẽ dùng factory này thay vì chạy full Host. ### 8. `AddDefaultTokenProviders()` không tồn tại trong `AddIdentityCore` **Triệu chứng:** Build fail `IdentityBuilder does not contain AddDefaultTokenProviders`. **Nguyên nhân:** `AddIdentityCore` là minimal variant, không include token providers (password reset, email confirmation). **Fix:** Bỏ call `AddDefaultTokenProviders()` nếu chưa cần. Khi cần password reset (Phase 4), chuyển sang `AddIdentity` hoặc add package `Microsoft.AspNetCore.Identity.UI`. ## OpenXml / ClosedXML (Form Engine Phase 2) ### 9. `SpaceProcessingModeValues.Preserve` namespace không tìm thấy **Triệu chứng:** `CS0103: The name 'SpaceProcessingModeValues' does not exist`. **Fix:** Dùng full path `DocumentFormat.OpenXml.SpaceProcessingModeValues.Preserve` và wrap trong `EnumValue<>`: ```csharp textElement.Space = new DocumentFormat.OpenXml.EnumValue( DocumentFormat.OpenXml.SpaceProcessingModeValues.Preserve); ``` ### 10. Placeholder `{{field}}` bị split giữa 2 `` elements **Vấn đề:** Word hay split text thành nhiều run (style, typo check), placeholder `{{giaTri}}` có thể bị chia thành `{{gia` + `Tri}}` nằm trong 2 `` khác nhau → regex replace miss. **Fix (đã implement trong DocxRenderer):** Iterate theo Paragraph, gom text của mọi `` trong cùng paragraph → replace → gán lại vào `` đầu + clear rest. Giữ run style của text đầu. ### 11. Word COM SaveAs PowerShell type conversion error **Triệu chứng:** `Cannot convert "..." value of type "psobject" to type "Object"` khi gọi `$doc.SaveAs([ref]$outPath, [ref]16)`. **Fix:** Dùng `SaveAs2` (không đòi ref parameters): ```powershell $doc.SaveAs2($outPath, 16) # 16 = wdFormatDocumentDefault (.docx) ``` ### 12. Word COM stuck/hang **Triệu chứng:** Script chạy không xong, process `WINWORD.EXE` còn nhưng CPU idle hoặc high. **Nguyên nhân:** Hidden dialog (activation, recovery, template warning) block COM. **Fix:** - Set `$word.DisplayAlerts = 0` trước khi mở file - Nếu stuck → `Get-Process WINWORD | Stop-Process -Force` - Fallback: dùng LibreOffice headless `soffice --headless --convert-to docx file.doc` ## System.Text.Json (ASP.NET Core 10) ### 13. Record constructor deserialization fail với Unicode **Triệu chứng:** POST JSON chứa ký tự tiếng Việt từ Windows bash/curl CLI → 400 "JSON value could not be converted to ... CreateSupplierCommand. Path: $.name". **Nguyên nhân:** Encoding CLI không đúng UTF-8 khi pass vào `curl -d '{...}'`. **Fix:** - Test qua file: `curl --data-binary @payload.json` (file lưu UTF-8 thật) - Không phải bug backend — API handle UTF-8 đúng qua axios/Swagger ## File operations ### 14. Dropbox sync có thể "revert" file đang edit **Triệu chứng:** Write file thành công, build pass, nhưng file thực tế vẫn là nội dung cũ. **Case cụ thể (Phase 1):** Program.cs Write thành công nhưng runtime chạy với default scaffold code. **Fix:** Sau Write file quan trọng → Read lại hoặc `head -5` để xác nhận nội dung. Nếu phát hiện revert → Write lại ngay. ### 15. `.gitignore` wwwroot/uploads/ vs wwwroot/templates/ **Quy ước:** - `wwwroot/uploads/` → **ignore** (user-uploaded files, không commit) - `wwwroot/templates/` → **commit** (template files là source of truth, phải version control) - `wwwroot/exports/` → ignore (rendered output, tạm) ## Dev workflow ### 16. Port conflict khi restart dev server **Triệu chứng:** `npm run dev` fail với `Port 8082 is in use`. **Nguyên nhân:** Background task trước chưa kill hẳn. **Fix:** `TaskStop` task cũ, hoặc kill process listening port: `netstat -ano | findstr :8082` → `taskkill /F /PID `. ### 17. EF migration tạo 3 file, COMMIT ĐỦ **Quy tắc:** Mỗi migration tạo: - `{timestamp}_{Name}.cs` — up/down - `{timestamp}_{Name}.Designer.cs` — model snapshot lúc đó - `ApplicationDbContextModelSnapshot.cs` — current snapshot (update mỗi lần) Commit đủ 3 file. Nếu thiếu, team khác `dotnet ef database update` sẽ fail. ## Checklist khi gặp bug mới 1. Build có pass không? Nếu fail → check using + package version 2. Log API startup có error ẩn không? → `tail` output file 3. File đã persist đúng chưa? → `head -5` verify 4. Nếu là package compat → thử downgrade về stable (không dùng preview/latest) 5. Nếu là TS error exotic → check tsconfig flags (`erasableSyntaxOnly`, `verbatimModuleSyntax`) 6. Nếu là EF expression tree error → tách logic ra ngoài query