dotnet runtime_error ai_generated true

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

Also available as: JSON · Markdown · 中文
85%Fix Rate
88%Confidence
1Evidence
2023-03-15First Seen

Version Compatibility

VersionStatusIntroducedDeprecatedNotes
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

Root Cause

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).

generic

中文

DbContext 实例不是线程安全的,多个线程同时使用它,通常是由于同步调用阻塞异步操作或 DI 注册不当(例如,使用 Singleton 代替 Scoped)导致的。

Official Documentation

https://learn.microsoft.com/en-us/ef/core/miscellaneous/connection-resiliency

Workarounds

  1. 90% success Ensure DbContext is registered as Scoped in DI (default). For ASP.NET Core: services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connectionString));
    Ensure DbContext is registered as Scoped in DI (default). For ASP.NET Core: services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connectionString));
  2. 85% success 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.
    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.
  3. 80% success Use DbContext pooling for high-throughput scenarios: services.AddDbContextPool<MyDbContext>(options => options.UseSqlServer(connectionString), poolSize: 128);
    Use DbContext pooling for high-throughput scenarios: services.AddDbContextPool<MyDbContext>(options => options.UseSqlServer(connectionString), poolSize: 128);

中文步骤

  1. Ensure DbContext is registered as Scoped in DI (default). For ASP.NET Core: services.AddDbContext<MyDbContext>(options => options.UseSqlServer(connectionString));
  2. 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.
  3. Use DbContext pooling for high-throughput scenarios: services.AddDbContextPool<MyDbContext>(options => options.UseSqlServer(connectionString), poolSize: 128);

Dead Ends

Common approaches that don't work:

  1. Changing DbContext registration to Singleton to reuse the same instance globally. 95% fail

    Singleton DbContext is not thread-safe and will cause the same error under concurrent requests; it also leads to stale data and memory leaks.

  2. Adding locks around all DbContext usage to serialize access. 85% fail

    Locks defeat the purpose of async I/O and can cause deadlocks or severe performance degradation under load.

  3. Setting DbContext.PoolSize to a large number without configuring AddDbContextPool. 90% fail

    PoolSize only applies when using DbContext pooling; without AddDbContextPool, it has no effect.