System.InvalidOperationException: 在此上下文实例上完成上一个操作之前,又启动了第二个操作。这通常是由不同线程同时使用同一 DbContext 实例引起的。
System.InvalidOperationException: A second operation was started on this context instance before a previous operation completed. This is usually caused by different threads concurrently using the same instance of DbContext.
ID: dotnet/ef-core-sync-query-on-async-context
版本兼容性
| 版本 | 状态 | 引入 | 弃用 | 备注 |
|---|---|---|---|---|
| Entity Framework Core 3.1 | active | — | — | — |
| Entity Framework Core 5.0 | active | — | — | — |
| Entity Framework Core 6.0 | active | — | — | — |
| Entity Framework Core 7.0 | active | — | — | — |
| Entity Framework Core 8.0 | active | — | — | — |
根因分析
DbContext 实例不是线程安全的,多个线程同时使用它,通常是由于同步调用阻塞异步操作或 DI 注册不当(例如,使用 Singleton 代替 Scoped)导致的。
English
A DbContext instance is not thread-safe and is being used concurrently from multiple threads, typically due to synchronous calls blocking async operations or improper DI registration (e.g., Singleton instead of Scoped).
官方文档
https://learn.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency解决方案
-
Ensure DbContext is registered as Scoped in DI (default). For ASP.NET Core: services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connectionString));
-
Avoid mixing sync and async calls on the same DbContext. Use async methods (e.g., ToListAsync() instead of ToList()) consistently. If you must use sync, create a separate DbContext instance.
-
Use DbContext pooling for high-throughput scenarios: services.AddDbContextPool<MyDbContext>(options => options.UseSqlServer(connectionString), poolSize: 128);
无效尝试
常见但无效的做法:
-
Changing DbContext registration to Singleton to reuse the same instance globally.
95% 失败
Singleton DbContext is not thread-safe and will cause the same error under concurrent requests; it also leads to stale data and memory leaks.
-
Adding locks around all DbContext usage to serialize access.
85% 失败
Locks defeat the purpose of async I/O and can cause deadlocks or severe performance degradation under load.
-
Setting DbContext.PoolSize to a large number without configuring AddDbContextPool.
90% 失败
PoolSize only applies when using DbContext pooling; without AddDbContextPool, it has no effect.