Files
solution-erp/.claude/skills/ef-core-migration/SKILL.md
pqhuy1987 e65578a821
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m55s
[CLAUDE] Docs: chot session 3 — PE polish iter 2 + domain rebrand + 5 gotcha moi
User request: 'Chot lai toan bo MD de sang session moi'.

Session 3 (2026-04-24) — ~15 commit feat/fix PE module + domain migration:
 - Domain 3 subdomain huypham.vn → solutions.com.vn E2E live
 - PE rename 'Phuong An' → 'Giai phap' + backfill DB
 - Menu tree inheritance extend Pe_*/PeWf_*
 - Accordion mutex Pe_* + sidebar w-72 + label nowrap
 - NavLink queryMatches (fix 2 leaf cung highlight)
 - PE detail flat layout: Panel 2 = 4 section, Panel 3 + approvals/history
 - Upload file dinh kem per-NCC (SupplierAttachmentsCell) + Bang so sanh tong
 - readOnly mode menu 'Duyet' (pendingMe=1)
 - HD move Lich su dieu chinh → Panel 3
 - Demo email rebrand @solutionerp.local → @solutions.com.vn + BackfillUserEmailDomain

Docs updated (6 file):
 - STATUS.md: +9 row Recently Done session 3. In Progress tick 10+ done. Phase
   hien tai = 'UX polish hoan thien, UAT-ready'.
 - HANDOFF.md: TL;DR session 3 summary. Priority 0 = 3 task MISSING cuoi
   (Designer UI, Y kien 4 phong ban, Export PDF). Login email moi.
 - gotchas.md: +5 entry (#34 NavLink query, #35 menu inheritance extend,
   #36 Vite env rebuild, #37 PS 5.1 ASCII, #38 Identity rename 4 field) +
   checklist debug +5 entry.
 - ef-core-migration SKILL: migration 13 AddPurchaseEvaluationCodeSequences
   + Phase 6 update section (ComparisonTable enum + BackfillUserEmail).
 - skills/README: ef-core-migration 13 migration label updated.
 - docs/changelog/sessions/2026-04-24-chot-session-3-pe-polish.md: session log
   15 commit + bugs + stats + next priorities session 4.

Memory project_solution_erp.md: Phase 6 iter 2 DONE. Domain rebrand DONE.
Session 4 priority 3 PE gap remaining.

Stats: 47 DB tables (+1 MaPhieu seq), ~113 endpoint (+3 PE attachments),
13 migrations, 38 gotchas, ~85 commits total.
2026-04-25 00:37:30 +07:00

9.7 KiB

name, description, when-to-use
name description when-to-use
ef-core-migration Tạo/sửa/revert EF Core 10 migration cho SOLUTION_ERP. Dùng khi thêm entity mới, thay đổi schema, rollback migration, debug DesignTimeDbContextFactory fail. Đã có 13 migration sẵn (Init → AddPurchaseEvaluationCodeSequences). Snapshot + Designer + Migration 3-file rule bắt buộc commit đủ.
thêm migration
EF Core migration
dotnet ef migrations add
revert migration
schema DB update
DbContext change
snapshot lỗi
DesignTimeDbContextFactory

EF Core Migration — SOLUTION_ERP

Context: .NET 10 + EF Core 10 + SQL Server. DbContext: ApplicationDbContextInfrastructure/Persistence/. Startup: SolutionErp.Api.

Migration history (12 migration hiện có)

# Name Tables added / changed
1 Init 7 Identity tables (AspNetUsers/Roles/UserRoles/...)
2 AddMasterData Suppliers, Projects, Departments
3 AddPermissions MenuItems, Permissions
4 AddForms ContractTemplates, ContractClauses
5 AddContractsWorkflow Contracts, ContractApprovals, ContractComments, ContractAttachments, ContractCodeSequences
6 AddNotifications Notifications
7 AddWorkflowTypeAssignments WorkflowTypeAssignments (legacy admin override)
8 AddVersionedWorkflows WorkflowDefinitions, WorkflowSteps, WorkflowStepApprovers + Contracts.WorkflowDefinitionId FK
9 AddContractDetailsAndChangelog 7 per-type Details (ThauPhu/GiaoKhoan/NhaCungCap/DichVu/MuaBan/NguyenTacNcc/NguyenTacDv) + ContractChangelogs
10 AddMasterCatalogs UnitsOfMeasure, MaterialItems, ServiceItems, WorkItems
11 AddRoleShortNameAndUserDepartment +Role.ShortName + User.DepartmentId/Position (cột thêm, không bảng mới)
12 AddPurchaseEvaluations 10 bảng module Duyệt NCC: PurchaseEvaluations + Suppliers + Details + Quotes + Approvals + Changelogs + Attachments + WorkflowDefinitions/Steps/StepApprovers
13 AddPurchaseEvaluationCodeSequences 1 bảng PurchaseEvaluationCodeSequences (Prefix PK, LastSeq) — atomic sequence cho MaPhieu format PE/{YYYY}/{A|B}/{Seq:D3}

Total: 47 bảng dbo + __EFMigrationsHistory. Xem docs/database/schema-diagram.md ERD đầy đủ.

Phase 7 pending (session sau):

  • AddPePaymentTermFields — tách PurchaseEvaluations.PaymentTerms JSON thành 6 column riêng
  • AddPeDepartmentOpinions — thêm 4 field Ý kiến phòng ban (Phê duyệt/CCM/MuaHàng/SM-PM) vào header hoặc table riêng

Phase 6 update (2026-04-24):

  • Enum PurchaseEvaluationAttachmentPurpose.ComparisonTable = 4 mới (file bảng so sánh tổng). Int column, không cần migration.
  • BackfillUserEmailDomainAsync trong DbInitializer — rename in-place 4 field Email/NormalizedEmail/UserName/NormalizedUserName. Idempotent.

Commands (chạy từ root repo)

Thêm migration mới

# Sau khi chỉnh Domain entity + EF config + DbContext DbSet
dotnet ef migrations add <MigrationName> `
  --project src/Backend/SolutionErp.Infrastructure `
  --startup-project src/Backend/SolutionErp.Api `
  --output-dir Persistence/Migrations

Naming convention: PascalCase, verb prefix:

  • Add<Thing> — thêm entity / bảng
  • Update<Thing> — thay đổi schema
  • Remove<Thing> — xóa column / table
  • Rename<Thing> — đổi tên

Apply migration to DB

# Dev: LocalDB SolutionErp_Dev
dotnet ef database update `
  --project src/Backend/SolutionErp.Infrastructure `
  --startup-project src/Backend/SolutionErp.Api

# Hoặc đơn giản: chạy API → DbInitializer tự Migrate
dotnet run --project src/Backend/SolutionErp.Api

Revert 1 migration (rollback)

# Rollback về migration trước đó (ví dụ về sau AddWorkflowTypeAssignments)
dotnet ef database update AddWorkflowTypeAssignments `
  --project src/Backend/SolutionErp.Infrastructure `
  --startup-project src/Backend/SolutionErp.Api

# Xóa file migration (3 file, xem dưới)
dotnet ef migrations remove `
  --project src/Backend/SolutionErp.Infrastructure `
  --startup-project src/Backend/SolutionErp.Api

Gen SQL script (review trước khi apply prod)

# Script từ 1 migration → latest
dotnet ef migrations script AddWorkflowTypeAssignments `
  --project src/Backend/SolutionErp.Infrastructure `
  --startup-project src/Backend/SolutionErp.Api `
  --output tmp/migration-7-to-8.sql

# Idempotent (check if exists trước mỗi operation)
dotnet ef migrations script --idempotent `
  --project src/Backend/SolutionErp.Infrastructure `
  --startup-project src/Backend/SolutionErp.Api

3-file rule (BẮT BUỘC commit đủ)

Mỗi migrations add sinh 3 file — phải commit đủ:

src/Backend/SolutionErp.Infrastructure/Persistence/Migrations/
├── {Timestamp}_{Name}.cs              ← migration logic (Up/Down)
├── {Timestamp}_{Name}.Designer.cs     ← model snapshot tại thời điểm migration
└── ApplicationDbContextModelSnapshot.cs  ← current model state (được overwrite mỗi migration)

Gotcha #17: Thiếu ModelSnapshot.csmigrations add kế tiếp sẽ sinh duplicate columns. Thiếu Designer.cs → không revert được.

Pitfalls thường gặp

P1 — DesignTime DbContext resolve fail

Triệu chứng: dotnet ef migrations addUnable to resolve service for type 'DbContextOptions<ApplicationDbContext>'.

Nguyên nhân: EF tooling chạy đứng ngoài runtime DI, cần factory riêng.

Fix: Đã có src/Backend/SolutionErp.Infrastructure/Persistence/DesignTimeDbContextFactory.cs. Nếu connection string thay đổi → update factory, không chỉ appsettings.json.

P2 — Table rename / column rename gen ra DROP + CREATE

Triệu chứng: Migration sinh DropColumn + AddColumn thay vì RenameColumn → mất data.

Fix: Sửa migration thủ công sang migrationBuilder.RenameColumn(...). Hoặc dùng [Column("newname")] attribute để EF tự detect rename.

P3 — Query filter (soft delete) khi thêm FK

Triệu chứng: Warning The entity type 'X' has a global query filter but referencing entity 'Y' doesn't. This may lead to inconsistent results.

Fix: Entity reference cũng phải có query filter:

builder.HasQueryFilter(x => !x.IsDeleted);

Hoặc dùng .IgnoreQueryFilters() cho query cần bypass.

P4 — FK cascade/restrict khác với expectation

Triệu chứng: Migration apply OK nhưng DELETE parent → cascade sang child (hoặc ngược lại restrict block).

Fix: Explicit config trong IEntityTypeConfiguration<T>:

builder.HasOne(x => x.Contract)
    .WithMany(c => c.Approvals)
    .HasForeignKey(x => x.ContractId)
    .OnDelete(DeleteBehavior.Cascade);  // hoặc Restrict, SetNull

Case study quan trọng: Contracts.WorkflowDefinitionIdWorkflowDefinitions.Id PHẢI dùng Restrict để protect HĐ cũ khi admin archive version (xem workflow-contract.md invariants).

P5 — Nullable reference type gen column NOT NULL

Triệu chứng: Property public string? Description → migration gen NOT NULL.

Fix: Check builder.HasQueryFilter chỗ config, hoặc explicit builder.Property(x => x.Description).IsRequired(false).

P6 — AddVersionedWorkflows duplicate seed

Case study hiện tại: Nếu thêm migration mới sau AddVersionedWorkflows, cẩn thận KHÔNG trigger DbInitializer.SeedWorkflowDefinitionsAsync 2 lần. Check if (!db.WorkflowDefinitions.Any()) trong DbInitializer.

Workflow khi thêm entity mới (example: add AuditLog)

1. Domain/AuditLogs/AuditLog.cs — tạo entity (BaseEntity hoặc AuditableEntity)
2. Infrastructure/Persistence/Configurations/AuditLogConfiguration.cs
   - ToTable("AuditLogs")
   - Unique index trên (EntityType, EntityId) nếu cần
3. Infrastructure/Persistence/ApplicationDbContext.cs
   - public DbSet<AuditLog> AuditLogs => Set<AuditLog>();
4. Application/Common/Interfaces/IApplicationDbContext.cs
   - DbSet<AuditLog> AuditLogs { get; }
5. dotnet ef migrations add AddAuditLogs
6. Review file .cs sinh ra → OK
7. dotnet ef database update → LocalDB apply
8. Test: dotnet run → check bảng được tạo
9. Commit [CLAUDE] Domain+Infra: add AuditLog entity + migration
10. ⚠️ UPDATE docs/database/schema-diagram.md (ERD + migration table)
11. UPDATE docs/STATUS.md nếu là work lớn

Apply prod (VPS)

# Trên VPS, sau khi deploy code:
# Option 1: auto migrate — DbInitializer.MigrateAsync() chạy khi API startup
# Option 2: manual script
dotnet ef migrations script <LastApplied> --idempotent --output migrate.sql
# Review migrate.sql → chạy qua sqlcmd:
sqlcmd -S .\SQLEXPRESS -d SolutionErp -U vrapp -P <pw> -i migrate.sql

Prod safety:

  • Backup trước: scripts/backup-sql.ps1
  • Dry-run idempotent script local trước
  • Test rollback plan: script from-N-to-M-1 sẵn

Code pointers

  • src/Backend/SolutionErp.Infrastructure/Persistence/ApplicationDbContext.cs — 24 DbSet
  • src/Backend/SolutionErp.Infrastructure/Persistence/DesignTimeDbContextFactory.cs — EF tooling factory
  • src/Backend/SolutionErp.Infrastructure/Persistence/DbInitializer.cs — seed + warn + migrate runtime
  • src/Backend/SolutionErp.Infrastructure/Persistence/Configurations/ — 24 IEntityTypeConfiguration
  • src/Backend/SolutionErp.Application/Common/Interfaces/IApplicationDbContext.cs — interface Application layer
  • docs/database/database-guide.md — conventions + migration workflow chi tiết
  • docs/database/schema-diagram.md — ERD 24 bảng
  • docs/gotchas.md #7, #17 — migration pitfalls