Insane Performance Boost in EF Core using Entity Framework Extensions
Learn how to replace slow EF Core bulk operations with Entity Framework Extensions
Connect with me:
🚀 Sponsored
Struggling with slow EF Core operations? With ZZZ Projects’ EF Core Extensions, you can boost performance like never before. Experience up to 14× faster Bulk Insert, Update, Delete, and Merge — and cut your save time by as much as 94%.
Introduction
Imagine that you’ve built a great application using .NET and Entity Framework Core. You start to process thousands - or tens of thousands - of records at once, and you notice a huge bottleneck: your application is getting slower and slower.
Standard EF Core is like a delivery driver who takes one package at a time to the same house, driving back and forth a thousand times. Entity Framework Extensions is the freight truck that delivers all 1,000 packages in a single trip.
In this article I’ll show you how to use the Entity Framework Extensions library by ZZZ Projects to significantly improve the performance of your .NET application. We’ll compare standard EF Core and EF Extensions on real use-case scenarios with actual benchmark numbers.
In this article, I’ll walk you through:
Installing the EF Extensions NuGet package
Why EF Core slows down at scale
BulkInsert, BulkInsertOptimized
BulkUpdate, BulkDelete
BulkMerge (upsert)
BulkSynchronize (upsert + delete missing)
BulkSaveChanges
🎬 Watch the full video here:
Why Standard EF Core Struggles at Scale
EF Core’s SaveChanges() is convenient - it tracks entity state and handles inserts, updates, and deletes automatically. But it generates one SQL statement per entity. Insert 10,000 products? That’s 10,000 round trips.
There are two reasons this kills performance:
1. The change tracker becomes a bottleneck. Tracking the state of 50,000 objects in memory consumes massive amounts of CPU and RAM. Entity Framework Extensions allows you to process data without the overhead of tracking every single property change - freeing your server to handle actual logic instead of managing memory.
2. Too many database round trips. You can see it directly in the console: EF Core floods it with individual INSERT statements, one per record. EF Extensions reduces all of those into a single bulk operation.
Installing Entity Framework Extensions
NuGet package:
dotnet add package Z.EntityFramework.Extensions.EFCoreOr search for Z.EntityFramework.Extensions.EFCore directly in the Visual Studio NuGet package manager.
The package supports .NET 10 and works with:
SQL Server
PostgreSQL
MySQL
Oracle
SQLite
Website and documentation:
https://entityframework-extensions.net - full docs, online benchmarks, and live examples.
Licensing: A free trial is available so you can evaluate the library end-to-end. For commercial projects, a paid license is required from ZZZ Projects. Check the pricing page once you’ve benchmarked it against your own workloads.
Benchmarks at a Glance
All results below are measured on 10,000 records against a PostgreSQL database.
Insert: ~2,800 ms → 177 ms (~16×)
Insert Optimized: ~2,800 ms → 72 ms (~39×)
Update: ~1,500 ms → 100 ms (~15×)
Delete: ~800 ms → 31 ms (~26×)
Merge (upsert): ~1,800 ms → ~100 ms (~18×)
Synchronize: ~1,700 ms → 126 ms (~13×)
BulkSaveChanges: ~1,400 ms → 425 ms (~3×)
Bulk Insert
EF Core (standard):
await context.Products.AddRangeAsync(products);
await context.SaveChangesAsync();EF Extensions:
await context.BulkInsertAsync(products);Result: ~2,800 ms → 177 ms.
Need even more speed and don’t need output values (like generated IDs) back? Use the optimized variant:
await context.BulkInsertOptimizedAsync(products);This skips the output value pass-back entirely, dropping the operation to 72 ms -roughly 39× faster than standard EF Core.
Bulk Update
EF Core:
foreach (var p in products)
p.Price *= 1.10m;
await context.SaveChangesAsync();EF Extensions:
foreach (var p in products)
p.Price *= 1.10m;
await context.BulkUpdateAsync(products);Result: ~1,500 ms → 100 ms - 15× faster.
Bulk Delete
EF Core:
context.Products.RemoveRange(products);
await context.SaveChangesAsync();EF Extensions:
await context.BulkDeleteAsync(products);Result: ~800 ms → 31 ms - over 25× faster.
Bulk Merge (Upsert)
A merge inserts new records and updates existing ones in a single operation. EF Extensions automatically detects what needs to be inserted vs. updated.
EF Core (manual):
foreach (var p in existing)
p.Price *= 1.15m;
context.Products.AddRange(incoming);
await context.SaveChangesAsync();EF Extensions:
foreach (var p in existing)
p.Price *= 1.15m;
var all = existing.Concat(incoming).ToList();
await context.BulkMergeAsync(all);Result (5,000 updates + 5,000 inserts = 15,000 total): ~1,800 ms → ~100 ms.
Bulk Synchronize
Synchronize goes one step further than Merge - it also deletes records that are not in your source list:
Rows that match the entity key are updated
Rows in the source but not in the database are inserted
Rows in the database but not in the source are deleted
Think of it as mirroring your source to the database.
EF Core (manual - a lot of code to get right):
var existingIds = source.Where(p => p.Id > 0).Select(p => p.Id).ToHashSet();
var toDelete = await context.Products
.Where(p => !existingIds.Contains(p.Id)).ToListAsync();
context.Products.RemoveRange(toDelete);
foreach (var p in source.Where(p => p.Id > 0))
context.Products.Update(p);
context.Products.AddRange(source.Where(p => p.Id == 0));
await context.SaveChangesAsync();EF Extensions:
await context.BulkSynchronizeAsync(source);Result: ~1,700 ms → 126 ms - and a fraction of the code.
Bulk SaveChanges
Already using the EF Core change tracker with a mix of Add, Update, and Delete? Drop in BulkSaveChangesAsync() as a near-zero-friction replacement:
await context.BulkSaveChangesAsync(); // instead of SaveChangesAsync()This is the easiest migration path if you have existing code and just want a performance lift.
Result on a mixed Add/Update/Delete workload: ~1,400 ms → 425 ms.
When Should You Reach for This?
You don’t need bulk operations for typical CRUD in a web app handling a handful of records at a time. But if any of these apply, the standard EF Core approach will hurt:
Importing data from CSVs or external feeds
Syncing records between systems
Running nightly price or inventory updates across thousands of rows
Handling data migrations
A one-line swap to BulkInsertAsync, BulkMergeAsync, or BulkSynchronizeAsync makes a real difference.
Key Takeaways
BulkInsertAsync - replaces AddRangeAsync + SaveChangesAsync, up to 16× faster
BulkInsertOptimizedAsync - skips output values for maximum insert speed (~40×)
BulkUpdateAsync - replaces foreach + SaveChangesAsync, 15× faster
BulkDeleteAsync - replaces RemoveRange + SaveChangesAsync, 25× faster
BulkMergeAsync - upsert in one call, auto-detects insert vs. update
BulkSynchronizeAsync - upsert + deletes records missing from source
BulkSaveChangesAsync - drop-in replacement for SaveChangesAsync with change tracker
Free trial available - commercial license required for production use
Resources
Entity Framework Extensions:
https://entityframework-extensions.net
EF Core Documentation: https://learn.microsoft.com/en-us/ef/core/
Connect with me:
Follow me on LinkedIn
Subscribe on YouTube
Want to sponsor this newsletter? Let’s work together →
