Site icon Chris Woody Woodruff

Batching Like a Boss: Using IDbContextFactory for High-Performance EF Core Updates

Batching Like a Boss: Using IDbContextFactory for High-Performance EF Core Update

I love receiving feedback on my blog posts! After sharing “Batching Updates and Inserts: Making EF Core Work Smarter, Not Harder,” I received some great comments, especially from MaxiTB on Mastodon, which I wanted to share.

Batch operations—whether inserting or updating multiple records—can quickly lead to performance issues, particularly when DbContext is misused. However, with the helpful support of IDbContextFactory and DbContext Pooling, you can optimize batch processing, ensuring your application remains fast, scalable, and thread-safe.

Let’s break it down and see how you can batch like a boss in EF Core!


The Problem: DbContext and Batch Operations

By default, EF Core allows batching of inserts and updates when possible. But if you’re calling SaveChanges() in a loop, you’re in for a bad time.

The Wrong Way: Multiple DbContext Saves

foreach (var order in orders)
{
    order.Status = "Processed";
    await context.SaveChangesAsync(); // Too many database calls!
}

This sends one update statement per record, causing tons of unnecessary database trips. Not great for performance.

The Better Way: Batch and Save Once

foreach (var order in orders)
{
    order.Status = "Processed";
}
await context.SaveChangesAsync(); // One batch update!

This combines updates into a single database transaction—MUCH faster!

But wait—there’s more! DbContext Pooling + IDbContextFactory can take this a step further.


Why IDbContextFactory<TContext>?

IDbContextFactory<TContext> creates new DbContext instances on demand rather than relying on scoped dependencies. This is super useful in batch operations because:

How to Register IDbContextFactory in EF Core

To use IDbContextFactory, update your DI configuration in Program.cs:

services.AddDbContextFactory<MyDbContext>(options =>
    options.UseSqlServer("Your_Connection_String")
);

Boom! Now, we have a factory to create fresh DbContext instances whenever needed.


Using IDbContextFactory for Batch Updates

Instead of keeping a single DbContext instance for all batch operations, let’s create a new one per batch using IDbContextFactory.

The Right Way: Use IDbContextFactory for Batch Updates

public class BatchUpdateService
{
    private readonly IDbContextFactory<MyDbContext> _contextFactory;

    public BatchUpdateService(IDbContextFactory<MyDbContext> contextFactory)
    {
        _contextFactory = contextFactory;
    }

    public async Task ProcessBatchUpdatesAsync(List<int> orderIds)
    {
        using var context = _contextFactory.CreateDbContext(); // Fresh context for this batch

        var orders = await context.Orders
            .Where(o => orderIds.Contains(o.Id))
            .ToListAsync();

        foreach (var order in orders)
        {
            order.Status = "Processed";
        }

        await context.SaveChangesAsync(); // Batch update, single trip to DB!
    }
}

Why This is Awesome:


How Different Databases Handle Batch Updates

Not all databases handle batch operations the same way. SQL Server does it well, but other databases? Not so much.

DatabaseBatch InsertsBatch UpdatesNotes
SQL ServerYesYesBest support for batching
PostgreSQLYesLimitedUpdates row-by-row unless optimized
MySQLLimitedNoBulk updates require custom logic
SQLiteNoNoOne statement at a time

Optimizing Batching for Non-SQL Server Databases

If you’re not using SQL Server, here are some ways to speed things up:

1. Use Raw SQL for Updates

await context.Database.ExecuteSqlRawAsync(
    "UPDATE Orders SET Status = 'Processed' WHERE Id IN ({0})", orderIds);

2. Process Large Batches in Chunks

const int batchSize = 100;
for (int i = 0; i < orders.Count; i += batchSize)
{
    var batch = orders.Skip(i).Take(batchSize).ToList();
    using var context = _contextFactory.CreateDbContext();
    context.Orders.UpdateRange(batch);
    await context.SaveChangesAsync();
}

This prevents loading too much data into memory at once!

3. Use Bulk Extensions for Faster Writes
For databases like MySQL or SQLite, EFCore.BulkExtensions is a lifesaver:

await context.BulkUpdateAsync(orders);

Final Takeaways: Batch Like a Pro!

Batch processing in EF Core doesn’t have to be slow. By combining:

You can level up your batch updates and keep your app blazing fast!

So, what’s your go-to strategy for batching updates? Have you tried IDbContextFactory in your EF Core projects? Let’s chat in the comments!

Exit mobile version