Entity Framework  

EF Core Bulk Operations

Introduction

In this article, we will learn about EF Core Bulk Operations.

Before we start, please take a look at my last article on Entity Framework.

Now, let's get started.
In EF Core, normal AddRange() , Update() , and Remove() Works fine for small datasets, but when you deal with thousands/millions of rows, performance tanks because EF sends one SQL command per row.

For that, you need bulk operations.

1. Built-in Options in EF Core

EF Core itself doesn’t provide true bulk operations out of the box, but you can optimize :

  • AddRange() / UpdateRange() / RemoveRange() β†’ fewer SaveChanges() calls, but still multiple SQL statements under the hood.

  • ExecuteUpdate() and ExecuteDelete() (EF Core 7+) β†’ allow set-based operations :

// Bulk Update
await context.Orders
    .Where(o => o.Status == "Pending")
    .ExecuteUpdateAsync(s => s
        .SetProperty(o => o.Status, "Completed")
        .SetProperty(o => o.UpdatedAt, DateTime.UtcNow));

// Bulk Delete
await context.Orders
    .Where(o => o.IsCancelled)
    .ExecuteDeleteAsync();

These are translated into single SQL UPDATE/DELETE queries, so very fast.

2. Third-Party Libraries for True Bulk Operations

When you need bulk insert/update/delete/merge, use libraries:

(a) EFCore.BulkExtensions (popular, free & open source)

EFCore_BulkExtension
using EFCore.BulkExtensions;

// Bulk Insert
await context.BulkInsertAsync(customers);

// Bulk Update
await context.BulkUpdateAsync(customers);

// Bulk Delete
await context.BulkDeleteAsync(customers);

// Bulk Merge (Insert or Update)
await context.BulkInsertOrUpdateAsync(customers);

βœ… Works with SQL Server, PostgreSQL, MySQL, and SQLite.
βœ… Supports batching, transactions, and temp tables.

(b) Z.EntityFramework.Extensions (paid, very powerful)

Z_EF_Extension2
// Bulk Insert
context.BulkInsert(customers);

// Bulk Update
context.BulkUpdate(customers);

// Bulk Delete
context.BulkDelete(customers);

// Bulk Merge
context.BulkMerge(customers);

βœ… Very fast (millions of rows).
βœ… Supports audit logs, filters, batch size, and includes Graphs.
❌ Commercial license after trial.

3. Raw SQL for Maximum Control

If you don’t want dependencies, just run SQL:

await context.Database.ExecuteSqlRawAsync(
    "UPDATE Orders SET Status = 'Completed' WHERE Status = 'Pending'");

βœ… Best performance, no EF tracking overhead.
❌ Lose EF change tracking & compile-time safety.

When to Use What

  • < 1000 rows β†’ normal AddRange/SaveChanges is fine.

  • 10K–1M rows β†’ use EFCore.BulkExtensions or ExecuteUpdate/Delete.

  • Extreme performance (millions+) β†’ raw SQL or Z.EntityFramework.Extensions.

Conclusion

In this article, I have tried to cover EF Core Bulk Operations.