.Net EF core DbContext.Save during multiple Async functions
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
Introduction
DbContext.SaveChangesAsync() works well in asynchronous code, but one rule matters more than anything else: a single DbContext instance is not thread-safe and must not run multiple operations at the same time. Most problems in “multiple async functions” scenarios come from sharing one context across parallel work.
The Safe Mental Model
Think of one DbContext as one unit of work. You can call asynchronous EF Core methods on it, but you must await each operation before starting the next one on the same instance.
Safe:
Unsafe parallel use of the same context:
That second example can throw because both queries try to use the same context concurrently.
async Does Not Mean “Safe to Share”
It is easy to think that because a method is async, the context can be passed around freely to many asynchronous functions. That is not the rule. The real rule is:
- asynchronous use is fine
- overlapping use on the same
DbContextinstance is not
This is why code such as Task.WhenAll around multiple EF Core calls often breaks when those calls share one scoped context.
Await Sequentially on One Context
If the operations belong to one transaction or unit of work, keep them sequential:
This is a normal pattern. One context tracks the changes, and one SaveChangesAsync() persists them at the end.
Use Separate Contexts for Real Parallelism
If you truly need parallel database work, create separate contexts:
Each parallel task gets its own context instance. That is the correct pattern when concurrency is actually required.
Save Once When It Makes Sense
Another common mistake is calling SaveChangesAsync() after every small update inside a larger workflow. Often it is cleaner and cheaper to:
- load entities
- modify them
- save once at the end
This reduces round-trips and keeps the unit of work coherent.
Use Transactions for Multi-Step Consistency
If several async steps must succeed or fail together, combine sequential context usage with a transaction:
This keeps the logic async while preserving consistency without resorting to unsafe shared parallel operations.
Common Pitfalls
- Starting multiple EF Core operations concurrently on the same
DbContext. - Passing one scoped context through several async methods and then wrapping them in
Task.WhenAll. - Calling
SaveChangesAsync()repeatedly when one save at the end of the unit of work would do. - Mistaking asynchronous code for thread-safe code.
- Using one context for long-running background or parallel jobs instead of creating separate instances.
Summary
- A single
DbContextinstance must not be used concurrently. - Asynchronous EF Core code is fine as long as each operation is awaited before the next begins on that context.
- Use separate contexts when you truly need parallel database work.
- Save once per unit of work when practical instead of saving after every tiny change.
- Add transactions when several async steps must commit together consistently.

