EF Core Bulk Insert Update - heartlanguage2024/heartlanguage2024 GitHub Wiki
Insert or update a large list of records (like 10,000 rows!) into a database using Entity Framework Core (EF Core).
1. 𧨠The Problem with the Old Way
The original method is very slow β³ when working with large amounts of data (like 10,000 records) because it:
- Uses
.Find()
one-by-one - Checks the database for each single record
- Triggers lots of database round-trips π
2. π§Ύ BEFORE: The Original Code (β Slow Version)
envSetupList.ForEach(item =>
{
var itemId = idProperty.GetValue(item);
var dbItem = dbContext.Set<T>().Local.FirstOrDefault(e =>
{
var entityId = idProperty.GetValue(e);
return Equals(entityId, itemId);
}) ?? dbContext.Set<T>().Find(itemId); // π΄ 1 DB call per item!
if (dbItem != null)
{
dbContext.Entry(dbItem).CurrentValues.SetValues(item); // βοΈ Update
}
else
{
dbContext.Set<T>().Add(item); // β Add new
}
});
3. π§ Step-by-Step Explanation of the Old Code
Line | What It Does | Emoji |
---|---|---|
itemId = idProperty.GetValue(item); |
Gets the ID from the current object | π |
dbContext.Set<T>().Find(itemId); |
Looks up that ID in the database | ππ¦ |
if (dbItem != null) |
If found, it means it's an update | π |
else |
Otherwise it's a new record | β |
β But for every single item, it does this again and again. Thatβs 10,000 times if we have 10,000 records = very slow π’
4. π AFTER: The Improved Version (β Fast Version)
// 1οΈβ£ Get all IDs from input list
var idsToCheck = envSetupList
.Select(item => item.Id)
.Where(id => id != null)
.Distinct()
.ToHashSet(); // β
Fast set lookup
// 2οΈβ£ Load all existing items from DB in one go!
var existingEntities = dbContext.Set<T>()
.Where(e => idsToCheck.Contains(e.Id)) // β
Translatable LINQ!
.ToDictionary(e => e.Id); // πΌ Convert to dictionary for fast access
// 3οΈβ£ Loop over input list just once
foreach (var item in envSetupList)
{
if (item.Id != null && existingEntities.TryGetValue(item.Id, out var dbItem))
{
dbContext.Entry(dbItem).CurrentValues.SetValues(item); // βοΈ Update
}
else
{
dbContext.Set<T>().Add(item); // β Add new
}
}
5. π Step-by-Step Explanation of the Improved Version
Line | What It Does | Emoji |
---|---|---|
.Select(item => item.Id) |
Grabs all the IDs in the list | πππ |
.Distinct() |
Removes duplicates | βπ |
.ToHashSet() |
Makes lookups very fast | π |
.Where(e => idsToCheck.Contains(e.Id)) |
Fetches all existing rows in 1 SQL query | π§ β‘ |
.ToDictionary(e => e.Id) |
So we can look up quickly during update | π |
foreach (...) |
Loop through your list | π |
TryGetValue(...) |
Checks if this item is already in the DB | π€β |
SetValues(item) |
Updates existing | π |
Add(item) |
Adds new if not found | β |
6. π Why the Improved Version is Faster
Feature | Old Way | Improved Way |
---|---|---|
DB Lookups | 1 per item = 10,000 calls! | Just 1 query to get all |
Loop Type | .ForEach on list |
foreach with dictionary lookup |
Existence Check | Find() (slow) |
TryGetValue() (super fast) |
Imagine if you go to the library to pick 10,000 books.
π
- β Old Way: You walk 10,000 times, one book each trip.
- β New Way: You give a list of books to the librarian and get all in one go! π
7. π Summary β When to Use Which
β Use the Improved Version when:
- You have thousands of records
- You want bulk update performance
- You want one DB call only
β οΈ The Old Version is okay if:
- You have very small datasets (< 100)
- You need per-record logic (logging, validation)
β οΈ Disabling Change Tracking in Entity Framework
Caution: The following code disables the change tracking feature in Entity Framework (EF), which may lead to issues with correctly tracking and triggering updates to dependent or calculated properties (such as SangiChohenText
). This can be problematic, especially if the property relies on navigation property changes or value propagation.
// Disable change tracking for improved performance during bulk operations
dbContext.ChangeTracker.AutoDetectChangesEnabled = false;
Key Considerations:
- π οΈ Performance vs. Accuracy: While disabling change tracking can enhance performance for bulk operations, it may result in EF failing to detect changes in related entities. This can cause issues where updates to properties, such as those calculated from navigation properties, are not propagated or reflected correctly.
- β οΈ Use with Caution: Only use this approach when you're certain that no changes to dependent properties are required during the operation. Re-enable change tracking afterward if further updates to your entities are necessary.