Open Closed

High CPU Usage and Build Performance Issues Due to Large Number of Entity Framework Core Migrations in ABP .NET Core Project #10795


User avatar
0
zubair_Latif created

We are using an ABP application built on .NET Core with Entity Framework Core migrations for database schema management. Over time, the project has accumulated a large number of migration files. We are currently experiencing a performance issue during the build process. Building the project consumes a significant amount of system resources, with CPU utilization frequently reaching 98%, resulting in slow build times and reduced development efficiency. Could you assist in identifying whether the large number of Entity Framework Core migrations could be contributing to this issue? Additionally, I would appreciate recommendations on best practices for managing a large migration history and optimizing build performance without affecting the existing database schema or deployment process.

Markdown supported.
Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)

6 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    • Yes, a very large number of EF Core migration files can contribute to slower builds and high CPU, because they are compiled as regular C# source files. More files = more work for the C# compiler, analyzers, IDE indexing, and incremental build checks. In ABP solutions this is most visible in the *.EntityFrameworkCore project that contains the Migrations/ folder.

    • However, it’s not ABP-specific; it’s primarily a “many C# files in one project” + “Roslyn/analyzers/IDE” build-time cost issue. ABP just commonly centralizes migrations in a single project, so the effect is easy to notice as that folder grows.

    What to do (practical, safe, ABP-friendly)

    1) Keep production safe: don’t delete migrations that are already deployed

    If you have existing environments already updated by those migrations, deleting or rewriting them will break upgrade paths unless you also manage a “re-baseline” process (below).

    2) Re-baseline (“squash”) migrations periodically (recommended for very long histories)

    This keeps your current schema the same, but replaces “hundreds/thousands of historical migrations” with:

    • one new baseline/initial migration that represents the current model
    • optionally: keep a small number of recent migrations after that baseline

    High-level approach (EF Core standard approach, works fine with ABP):

    1. Ensure all environments are migrated up to the latest migration.
    2. Create a new baseline migration from the current model (this becomes the new “initial” going forward).
    3. Archive the old migrations (keep them in source control history or in a separate folder/repo if you must keep them for audit).
    4. For existing databases, the new baseline must be treated as already applied (typical approach is to ensure the __EFMigrationsHistory contains the baseline migration id, or keep the old history but stop compiling old migrations).

    This approach is common EF Core practice; ABP does not require “all historical migrations” to remain forever, as long as your deployment process can still upgrade existing databases.

    3) If you must keep all old migrations, stop compiling them in the main project

    You can move old migrations into a separate project (e.g., MyApp.Migrations.Archive) so day-to-day builds don’t compile them.

    • Keep only the “active” migrations in the main EntityFrameworkCore project.
    • This preserves your history without punishing build performance.

    4) Make sure migrations are not executed during app startup in production

    This doesn’t affect build CPU, but it’s a best practice for ABP deployments (and avoids other performance problems):

    • Run migrations via DbMigrator/manual pipeline, not on application startup.

    Notes on “optimizing build CPU”

    • After you reduce the number of compiled migration files (by re-baselining or archiving), CPU usage during build typically drops significantly.
    • If CPU is still maxed, it’s often due to analyzers/source generators running across many files. Reducing file count is still the biggest win.

    References (validated)

    • https://docs.abp.io/en/abp/latest/Entity-Framework-Core-Migrations
    • https://docs.abp.io/en/abp/latest/Deployment/Optimizing-Production
    • https://abp.io/community/articles/wa24j28e

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Yes, a large Migrations/ folder is very likely the main cost here. Each migration ships with a *.Designer.cs file whose BuildTargetModel embeds the full model snapshot at that point in time. With a big ABP model plus your own entities that's easily thousands of lines per file, so hundreds of migrations means hundreds of full snapshots the compiler has to parse and build every time. That's what pushes compile time (and CPU) up as the folder grows.

    The safe way to fix this without touching your existing schema is to re-baseline (squash) the migrations. EF Core still has no built-in squash command (see https://github.com/dotnet/efcore/issues/2174), so it's a manual step:

    1. Make sure every environment/database is already migrated up to the latest migration.
    2. Delete the Migrations folder in your *.EntityFrameworkCore project.
    3. Create a single new baseline migration:
    dotnet ef migrations add Initial
    
    1. For each existing database, insert one row into __EFMigrationsHistory so EF treats the new baseline as already applied (the tables already exist, so it must not run). Don't clear the existing rows — leave them in place. Example for SQL Server (adjust for your DB provider):
    INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion])
    VALUES (N'20260715120000_Initial', N'9.0.0');
    

    Use the exact MigrationId of the file EF just generated, and your EF Core version for ProductVersion.

    New/empty databases are unaffected — the baseline migration runs normally and creates everything.

    A few things to keep in mind:

    • Why keep the old history rows: EF only re-runs a migration if its ID exists in your project but not in __EFMigrationsHistory. After step 2 the old IDs are gone from your project, so they'll never re-run. Leaving them in the table keeps your audit/rollback trail and avoids any window where a stray Migrate() tries to re-create existing tables.
    • If any old migration created objects EF can't model (views, triggers, functions via raw migrationBuilder.Sql(...)), copy those into the new baseline so fresh databases still get them. One-off data fixes or column renames from the history don't need to be carried over — a new database only needs the final schema.
    • If you run the same app across multiple customer/tenant databases (or have offline installs), bring them all up to the same latest migration before you switch. A database that's left behind can't be upgraded from the new baseline anymore. The schema stays the same, but the cutover itself is a deployment step worth planning.

    Keep using your .DbMigrator project as the deployment entry point as usual — this only changes how many migration files get compiled, not how you apply migrations or seed data.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    zubair_Latif created

    Do we need to repeat this process periodically? After a certain period, the issue seems to reoccur, requiring us to perform the same steps again

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    There's no fixed schedule for it, and no permanent fix on EF's side — EF doesn't compress or archive old migrations for you. As long as you keep adding migrations to the same project that gets compiled on every build, the cost grows back, since each migration carries its own full generated model snapshot. So instead of a set interval, re-baseline again when your build times noticeably climb; a major release is a natural point to do it.

    What slows the growth in between is consolidating migrations while they're still local — before they've been applied to any database you can't freely recreate (staging, shared dev, production) and before another branch depends on them. In that window you can collapse several small migrations into one: remove them in reverse order and re-add a single combined migration.

    dotnet ef migrations remove
    

    Two caveats when you do this: if a migration is already applied to your local database, roll it back (or recreate the DB) first, or remove will refuse; and any handwritten SQL or data migration won't reappear in the regenerated file, so carry those over by hand.

    The final schema stays the same either way — this is only about how many migration files the compiler has to process.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    zubair_Latif created

    I have one additional question. Our development environment contains more Entity Framework Core migrations than the production environment because migrations are only applied to production after a feature has been fully tested and approved for deployment.

    In this scenario, what would be the recommended approach? Is there a specific solution for managing this situation, or should we only archive the migrations that have already been applied to the production database while keeping the pending development migrations intact?

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
  • User Avatar
    0
    maliming created
    Support Team Fullstack Developer

    Hi,

    Keeping the pending migrations and folding only the already-deployed ones into a baseline is the right idea in spirit, but leaving the pending files in place while you squash the earlier ones doesn't work cleanly. EF orders migrations by their MigrationId timestamp, not by file or commit order — a freshly generated baseline gets a newer timestamp than your existing pending migrations, so those pending ones would sort before the baseline instead of after it, and the chain breaks. The model snapshot and history bookkeeping are easy to get wrong here too.

    The clean approach is to re-baseline from a point where there are no pending migrations at all — generate it against the code that matches production (your production release branch/commit), not your dev head. In practice you do it once the currently-pending features have been promoted, so dev and production line up:

    • Delete the Migrations folder and run dotnet ef migrations add Initial on that aligned state.
    • Insert its row into __EFMigrationsHistory on each existing database (they're all at that same point now).
    • Carry on normal development on top of the baseline — new feature migrations accumulate after it and get promoted to production as usual.

    You can squash only the already-deployed prefix while keeping the dev-ahead migrations, but that means manually fixing the baseline's MigrationId ordering, the model snapshot, and any custom SQL or data steps inside those migrations — error-prone enough that lining dev and production up first is the safer route.

    Your approval → test → deploy process itself doesn't change; this is a one-time re-baseline done at a release boundary.

    Thanks

    Markdown supported.
    Copy, paste, or drag & drop images and files (max 100 MB per file, 100 MB total per post)
Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.8.0-preview. Updated on September 28, 2026, 11:44
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.