DBContext Error: A second operation started on this context before a previous operation completed.

Issue Description

Entity Framework Core runtime gives the below error,

A second operation started on this context before a previous operation completed. This is usually caused by different threads using the same instance of DbContext, however, instance members are not guaranteed to be thread-safe. 

I observed this issue in ASP.NET Core 3.1 and also .NET 6.0 version released recently.

However similar issue might also be found on the lower version of .NET Core.

Resolution

This error I found to be due to a concurrency issue associated with DBContext.

Please note that the DBContext object is not thread-safe.

It’s the implementor’s responsibility to implement the concurrency while using it.

If using ASP.NET Core

  • Use DbContext using a dependency injection Container(Recommended).
  • Add the DbContext type to the service container by using the AddDbContext method with Scoped lifetime (Recommended ).

Example – Update Startup.cs as below,

services.AddDbContext<EmployeeContext>(options =>
     {  
       options.UseSqlServer(Configuration.GetConnectionString("EmployeeDB");
     }
            

Make sure to use scoped DBContext objects Using AddDbContext(..) which ensures that you are registering the DBCOntext as Scoped. i.e every new request will use the new service instance and hence the new DBContext.

A second operation started on this context

Using a Repository for DBContext?

If the repository is being used for DI DBContext, then it’s important that the Repository instance is also Scoped lifetime in the API pipeline container.

Sometimes many other coding issues could also cause this issue.

If using Asynchronous operation?

Please make sure to await all your Asynchronous calls.

Please do not mix sync and async calls without proper design.

Asynchronous methods without await could lead to database access in a non-blocking way thereby may cause synchronization issues.

Please check this link on Async Programming Best Practices.

Example:

[HttpGet("{id}")]
        public async Task<IActionResult> OnGetAsync(int? id)
        {
            if (id == null)
            {
                return NotFound();
            }
            EmployeeDb db = await _employeeContext.EmployeeDb.FindAsync(id);
            if (db == null)
            {
                return NotFound();
            }
            return Ok(db);
        }

If using .NET Core Console or Form app

If using the .NET Core console or Forms app similar approach can be followed.

Please register the DBContext as scoped by calling AddDbContext as below,

var builder = new HostBuilder()
                .ConfigureServices((hostContext, services) =>
                {
                     services.AddLogging(configure => configure.AddConsole())
                     .AddTransient<MyApplication>()
                     .AddDbContext<EmployeeContext>(options =>
                     {
                         options.UseSqlServer("Server=localhost\\SQLEXPRESS;Database=master;Trusted_Connection=True;");
                     });
                })

For more details, please refer below,

Configuring DBContext – Best practices

Additionally, please visit the below article for other best practices on configuring the DBContext

That’s all! Happy coding!

Does this help you fix your issue?

Do you have any better solutions or suggestions? Please sound off your comments below.



Please bookmark this page and share it with your friends. Please Subscribe to the blog to receive notifications on freshly published(2024) best practices and guidelines for software design and development.



One thought on “DBContext- A second operation started on this context before a previous operation completed

  1. A second operation started on this context before a previous operation completed.
    This is usually caused by different threads using the same instance of DbContext.
    For more information on how to avoid threading issues

Leave a Reply

Your email address will not be published. Required fields are marked *