# EFCore.BulkExtensions: bulk CRUD for EF Core on five SQL databases

> EFCore.BulkExtensions replaces row-by-row SaveChanges with provider-native bulk copy for Insert, Update, Delete and Read. It is dual licensed, and the free tier has conditions.

**borisdj/EFCore.BulkExtensions** — Entity Framework EF Core efcore Bulk Batch Extensions with BulkCopy in .Net for Insert Update Delete Read (CRUD), Truncate and SaveChanges operations on SQL Server, PostgreSQL, MySQL, SQLite, Oracle

- Repository: https://github.com/borisdj/EFCore.BulkExtensions
- Website: https://codis.tech/efcorebulk
- Stars: 4,001 · Forks: 637
- Language: C#
- License: NOASSERTION
- Published: 2026-09-23 · Updated: 2026-09-23 · Language: en
- Canonical page: https://hysenlabs.com/projects/borisdj-efcore-bulkextensions

## The row-by-row problem EFCore.BulkExtensions targets

EF Core is good at tracking entities and writing them one statement at a time. That model breaks down when the set is large. A loop over Add plus a single SaveChanges produces one INSERT per entity, and the same holds for updates and deletes. The round trips dominate the runtime.

EFCore.BulkExtensions exists to move those operations onto the database's own bulk path. The README describes the library as EntityFrameworkCore extensions that offer "Bulk operations (super fast, en masse DB Protocol): Insert, Update, Delete, Read, Upsert, Sync, SaveChanges" plus batch operations and a Truncate addition. The intended user is a .NET team already on EF Core that has hit a wall on volume: imports, synchronisation jobs, data migrations, anything that touches tens of thousands of rows in one pass. The README names customers from small and medium businesses up to large corporations, including big-data and fintech companies with large datasets.

It is not a replacement for EF Core. It is a set of extension methods on top of the same DbContext, so the mapping, the model configuration and the connection handling stay where they are.

## How the provider adapters actually move rows

The repository is split by provider, and that layout reflects the design. There are separate projects for SqlServer, PostgreSql, MySql, Oracle and Sqlite alongside a Core project, and the README states the main NuGet package covers all databases while single-provider packages carry an adapter suffix.

Each adapter uses a different native mechanism, and the README is explicit about which. SQL Server uses SqlBulkCopy for insert, and for update or delete it does a bulk insert followed by a raw SQL MERGE. PostgreSQL uses COPY BINARY combined with ON CONFLICT for update. MySQL uses MySqlBulkCopy combined with ON DUPLICATE. Oracle uses OracleBulkCopy combined with MERGE. SQLite is the outlier: the README says it has no copy tool, so the library falls back to plain SQL combined with UPSERT.

That difference matters when you reason about behaviour. On SQL Server, PostgreSQL, MySQL and Oracle the write goes through a copy protocol and the update semantics are expressed as a database-side merge or conflict clause. On SQLite the same API is backed by ordinary SQL, so the performance profile is not comparable to the other four. The README does not publish per-provider timings, so treat the SQLite path as functionally equivalent rather than equally fast.

## Installing EFCore.BulkExtensions and running a first bulk insert

The package is on NuGet. The README gives the Package Manager Console command, which is the shortest route in Visual Studio:

```bash
Install-Package EFCore.BulkExtensions
```

If you want the smaller single-provider package instead, the README says the specific ones are the main package name plus an adapter suffix, for example EFCore.BulkExtensions.SqlServer. That is the package to pick when you only target one database and want to avoid pulling in the others.

The README lists Insert, Update, Delete, Read, Upsert, Sync and SaveChanges as the bulk operations, and the extension methods hang off the DbContext under those names. The call goes through the bulk path for your provider rather than emitting one statement per entity, so you should see a single bulk operation in the database's own tooling rather than a stream of individual INSERTs.

One practical note from the README: bulk tests cannot use UseInMemoryDb, because the InMemory provider does not support relational-specific methods. If your test suite runs against the InMemory provider, the bulk methods will not behave there. The README suggests SQL Server Developer or Express, LocalDb, or another adapter for test runs.

## Where the bulk path stops being the right answer

The bulk API trades EF Core's normal change tracking for throughput. That is the whole point, and it is also the limitation. If you rely on interceptors, on the tracked state of the entities after the call, or on per-row events firing, the bulk path is not a drop-in substitute for SaveChanges and the README does not claim it is.

The provider split is a second boundary. The README lists SQL Server, PostgreSQL, MySQL, Oracle and SQLite. Anything else is out of scope. SQLite is also the weakest of the five on the write path, since the README states it has no copy tool and the library uses plain SQL with UPSERT instead. If SQLite is your production database and volume is the reason you are here, the mechanism itself caps how much you gain.

The batch operations are another moving part. The README marks batch Update and Delete as deprecated from EF8, noting that v7 and later have native ExecuteUpdate and ExecuteDelete. So on EF Core 8 and above, reach for the built-in methods for chunked updates and deletes and keep this library for the bulk copy operations.

Finally, the README does not document rollback behaviour for the bulk methods, so do not assume a failed bulk call leaves the database untouched. Verify that against your provider before you put one inside a larger transaction.

## Alternatives: EF Core 8 ExecuteUpdate and raw ADO.NET

The most direct alternative is what EF Core itself now ships. ExecuteUpdate and ExecuteDelete, native from EF Core 7, run a set-based statement without loading entities. They cover the chunked update and delete cases that this library's batch operations used to handle, which is exactly why the README marks those batch operations deprecated from EF8. The difference in approach is scope: ExecuteUpdate and ExecuteDelete express a single set-based command, while EFCore.BulkExtensions streams a set of entities through a provider copy protocol for insert, update, upsert and read. If your problem is one UPDATE with a WHERE clause, the built-in method is enough. If your problem is loading a hundred thousand rows, it is not.

The other alternative is going straight to the provider. SqlBulkCopy, COPY BINARY, MySqlBulkCopy and OracleBulkCopy are all reachable from ADO.NET without an EF Core layer. You would write the DataTable or the reader yourself and manage the mapping by hand. EFCore.BulkExtensions wraps that work and keeps you inside the DbContext, which is the reason to take the dependency. The cost is that you inherit the library's provider coverage and its license terms rather than your own.

## Licence, maintenance and what an upgrade costs

The licence is the part to read carefully. The repository's LICENSE.txt is the source of truth, and the README describes the project as dual licensed under a cFOSS model, meaning conditionally free open source. The README states plainly: if you do not meet the criteria for free usage under the community license, you have to buy a commercial one. It also notes that if you are eligible for free usage but want active support, a Starter licence is available. This is not an MIT package, and the search phrase efcore bulkextensions mit does not match what the README says about the licence. Check the criteria against your organisation before you ship.

On maintenance, the last push to the default branch was on 2026-08-14, which is recent. The most recent release listed is 8.1.2 for EF Core 8.0, dated 2024-12-01, with older lines at 6.5.6 and v5.0.5. The README states the latest version is using EF Core 10, so the versioning tracks EF Core major versions rather than a single rolling release. That is the upgrade cost you should budget for: when you move EF Core major versions, you move the matching EFCore.BulkExtensions release, and the package you install has to line up with the provider adapter you use. The README does not describe a supported-version matrix or an upgrade guide, so confirm the pairing on NuGet before a major bump.

## Conclusion

Adopt EFCore.BulkExtensions when you load or mutate large entity sets through EF Core and want the provider's native copy path instead of per-row statements. Do not adopt it if you only write a handful of rows per request, or if you depend on the EF InMemory provider, which the README says cannot run the bulk tests. Before committing, verify two things yourself: which license tier your organisation falls into, since the project is dual licensed and the free community license is conditional, and whether your target provider is one of the five the README lists, because the adapter packages are split per database. The main package is on NuGet, so a first check is one install and one BulkInsertAsync call against a real database.

## FAQ

### How do I use EFCore.BulkExtensions?

Install the NuGet package, then call the extension methods on your DbContext, for example BulkInsertAsync for an insert. The README lists Insert, Update, Delete, Read, Upsert, Sync and SaveChanges as the available bulk operations.

### Is EFCore.BulkExtensions free?

It is dual licensed under a cFOSS model. The README says that if you do not meet the criteria for free usage under the community license, you have to buy a commercial one, so free use is conditional rather than unconditional.

### What is the EFCore.BulkExtensions alternative if I cannot use it?

EF Core 7 and later ship native ExecuteUpdate and ExecuteDelete, which the README itself points to as the replacement for the deprecated batch operations. For the copy path itself you would fall back to the provider's own bulk copy API through ADO.NET.

## Sources

- [borisdj/EFCore.BulkExtensions on GitHub](https://github.com/borisdj/EFCore.BulkExtensions)
- [Issues](https://github.com/borisdj/EFCore.BulkExtensions/issues)
- [Project website](https://codis.tech/efcorebulk)
- [README](https://github.com/borisdj/EFCore.BulkExtensions/blob/master/README.md)
- [Releases](https://github.com/borisdj/EFCore.BulkExtensions/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/borisdj-efcore-bulkextensions
