A high-performance, provider-agnostic bulk insert extension for Entity Framework Core 8+. Supports SQL Server, PostgreSQL, SQLite, MySQL and Oracle.
Its main purpose is to provide a fast way to perform simple bulk inserts in Entity Framework Core applications.
For now, it does not support navigation properties, complex types, owned types, shadow properties, or inheritance, but they are in the roadmap.
Install the NuGet package for your database provider:
# For SQL Server Install-Package PhenX.EntityFrameworkCore.BulkInsert.SqlServer # For PostgreSQL Install-Package PhenX.EntityFrameworkCore.BulkInsert.PostgreSql # For SQLite Install-Package PhenX.EntityFrameworkCore.BulkInsert.Sqlite # For MySql Install-Package PhenX.EntityFrameworkCore.BulkInsert.MySql # For Oracle Install-Package PhenX.EntityFrameworkCore.BulkInsert.Oracle
Register the bulk insert provider in your DbContextOptions
:
services.AddDbContext<MyDbContext>(options => { options // .UseSqlServer(connectionString) // or UseNpgsql or UseSqlite, as appropriate .UseBulkInsertPostgreSql() // OR .UseBulkInsertSqlServer() // OR .UseBulkInsertSqlite() // OR .UseBulkInsertMySql() // OR .UseBulkInsertOracle() ; });
// Asynchronously await dbContext.ExecuteBulkInsertAsync(entities); // Or synchronously dbContext.ExecuteBulkInsert(entities);
// Common options await dbContext.ExecuteBulkInsertAsync(entities, options => { options.BatchSize = 1000; // Set the batch size for the insert operation, the default value is different for each provider }); // Provider specific options, when available, example for SQL Server await dbContext.ExecuteBulkInsertAsync(entities, (SqlServerBulkInsertOptions o) => // <<< here specify the SQL Server options class { options.EnableStreaming = true; // Enable streaming for SQL Server }); // Provider specific options, supporting multiple providers await dbContext.ExecuteBulkInsertAsync(entities, o => { o.MoveRows = true; if (o is SqlServerBulkInsertOptions sqlServerOptions) { sqlServerOptions.EnableStreaming = true; } else if (o is MySqlBulkInsertOptions mysqlOptions) { mysqlOptions.BatchSize = 1000; } });Returning inserted entities
await dbContext.ExecuteBulkInsertReturnEntitiesAsync(entities);Conflict resolution / merge / upsert
Conflict resolution works by specifying columns that should be used to detect conflicts and the action to take when a conflict is detected (e.g., update existing rows), using the onConflict
parameter.
Match
property and must have a unique constraint in the database.Update
property. If not specified, the default action is to do nothing (i.e., skip the conflicting rows).Where
or the RawWhere
property. If not specified, the update action will be applied to all conflicting rows.await dbContext.ExecuteBulkInsertAsync(entities, onConflict: new OnConflictOptions<TestEntity> { Match = e => new { e.Name, // ...other columns to match on }, // Optional: specify the update action, if not specified, the default action is to do nothing // Excluded is the row being inserted which is in conflict, and Inserted is the row already in the database. Update = (inserted, excluded) => new TestEntity { Price = inserted.Price // Update the Price column with the new value }, // Optional: specify the condition for the update action // Excluded is the row being inserted which is in conflict, and Inserted is the row already in the database. // Using raw SQL condition RawWhere = (insertedTable, excludedTable) => $"{excludedTable}.some_price > {insertedTable}.some_price", // OR using a lambda expression Where = (inserted, excluded) => excluded.Price > inserted.Price, });
Benchmark projects are available in the tests/PhenX.EntityFrameworkCore.BulkInsert.Benchmark
directory. Run them to compare performance with raw bulk insert methods and other libraries (https://github.com/borisdj/EFCore.BulkExtensions and https://entityframework-extensions.net/bulk-extensions), using optimized configuration (local Docker is required).
Legend :
PhenX_EntityFrameworkCore_BulkInsert
: this libraryRawInsert
: naive implementation without any library, using the native provider API (SqlBulkCopy for SQL Server, BeginBinaryImport for PostgreSQL, raw inserts for SQLite)Z_EntityFramework_Extensions_EFCore
: https://entityframework-extensions.net/bulk-extensionsEFCore_BulkExtensions
: https://github.com/borisdj/EFCore.BulkExtensionsLinq2Db
: https://github.com/linq2db/linq2dbPostgreSQL results with 500 000 rows :
SQLite results with 500 000 rows :
MySQL results with 500 000 rows :
Where are the SQL Server and Oracle benchmarks? You can run them yourself.
Contributions are welcome! Please open issues or submit pull requests for bug fixes, features, or documentation improvements.
MIT License. See LICENSE for details.
RetroSearch is an open source project built by @garambo | Open a GitHub Issue
Search and Browse the WWW like it's 1997 | Search results from DuckDuckGo
HTML:
3.2
| Encoding:
UTF-8
| Version:
0.7.4