efcore: [BUG] 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.

I am using ef core 3.1.5 and asp.net core 3.1 and am having this constant issue around a function that sets a view bag in the edit and create methods.

Strange thing is this only happens in production on the hosted server, i dont get this error locally

    services.AddDbContext<MISDBContext>
        (options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Scoped);
    
        public async Task<int> GetNotificationsCount() {
            var userId =  GetCurrentTennantId().Result;
            //the not should show all the notifcations should only check the desnitation user id
            return _context.Notifications.Where(w => w.SharedTo == userId.ToString()).Count();
        }
 

This setup view bag would be called on functions like edit and create

 public async void SetupViewBags() {
 ViewBag.NotificationCount = await GetNotificationsCount();

   }

For Example I would call it as such

  // GET: MISObjects/Create
    public IActionResult Create() {
        SetupViewBags();

        return View();
    }

Got Exceptions? Include both the message and the stack trace

info: Microsoft.AspNetCore.Hosting.Diagnostics[2] Request finished in 165.3482ms 200 application/javascript info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (1ms) [Parameters=[@__ToString_0=‘?’ (Size = 450)], CommandType=‘Text’, CommandTimeout=‘30’] SELECT COUNT(*) FROM [MISobject] AS [m] WHERE (([m].[OIC_1] = @__ToString_0) OR ([m].[OIC_2] = @__ToString_0)) AND (([m].[isDeleted] = CAST(0 AS bit)) AND ([m].[isActive] = CAST(1 AS bit))) info: Microsoft.AspNetCore.Hosting.Diagnostics[2] Request finished in 163.0282ms 200 application/javascript info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (1ms) [Parameters=[], CommandType=‘Text’, CommandTimeout=‘30’] SELECT [a].[FirstName], [a].[LastName], [a].[Id] FROM [AspNetUsers] AS [a] info: Microsoft.EntityFrameworkCore.Database.Command[20101] Executed DbCommand (1ms) [Parameters=[], CommandType=‘Text’, CommandTimeout=‘30’] SELECT [a].[Id], [a].[AccessFailedCount], [a].[ConcurrencyStamp], [a].[Email], [a].[EmailConfirmed], [a].[FirstName], [a].[LastName], [a].[LockoutEnabled], [a].[LockoutEnd], [a].[NormalizedEmail], [a].[NormalizedUserName], [a].[PasswordHash], [a].[PhoneNumber], [a].[PhoneNumberConfirmed], [a].[SecurityStamp], [a].[TennantId], [a].[TwoFactorEnabled], [a].[UserName] FROM [AspNetUsers] AS [a] fail: Microsoft.EntityFrameworkCore.Query[10100] An exception occurred while iterating over the results of a query for context type ‘MISSystem.Dal.MISDBContext’. System.InvalidOperationException: 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 with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.MoveNextAsync() System.InvalidOperationException: 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 with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.AsyncEnumerator.MoveNextAsync() info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2] Sending file. Request path: ‘/font-awesome/js/all.js’. Physical path: ‘D:\GitMaster\MIS\uno\MISSystem.WEB\MISSystem.Web\MISSystem.Web\wwwroot\font-awesome\js\all.js’ info: Microsoft.AspNetCore.Hosting.Diagnostics[2]

For dotnet ef and PMC, share the --verbose output An exception occurred while iterating over the results of a query for context type ‘MISSystem.Dal.MISDBContext’. System.InvalidOperationException: 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 with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.Enumerator.MoveNext() System.InvalidOperationException: 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 with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.Enumerator.MoveNext() Unhandled exception. System.InvalidOperationException: 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 with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable1.Enumerator.MoveNext() at System.Linq.Enumerable.Single[TSource](IEnumerable1 source) at lambda_method(Closure , QueryContext ) at Microsoft.EntityFrameworkCore.Query.Internal.QueryCompiler.Execute[TResult](Expression query) at Microsoft.EntityFrameworkCore.Query.Internal.EntityQueryProvider.Execute[TResult](Expression expression) at System.Linq.Queryable.Count[TSource](IQueryable1 source) at MISSystem.Web.Controllers.MISObjectsController.GetNotificationsCount() in D:\GitMaster\MIS\uno\MISSystem.WEB\MISSystem.Web\MISSystem.Web\Controllers\MISObjectsController.cs:line 115 at MISSystem.Web.Controllers.MISObjectsController.SetupViewBags() in D:\GitMaster\MIS\uno\MISSystem.WEB\MISSystem.Web\MISSystem.Web\Controllers\MISObjectsController.cs:line 405 at System.Threading.Tasks.Task.<>c.<ThrowAsync>b__139_1(Object state) at System.Threading.QueueUserWorkItemCallback.<>c.<.cctor>b__6_0(QueueUserWorkItemCallback quwi) at System.Threading.ExecutionContext.RunForThreadPoolUnsafe[TState](ExecutionContext executionContext, Action1 callback, TState& state) at System.Threading.QueueUserWorkItemCallback.Execute() at System.Threading.ThreadPoolWorkQueue.Dispatch() at System.Threading._ThreadPoolWaitCallback.PerformWaitCallback()

Further technical details

EF Core version: Database provider: (e.g. Microsoft.EntityFrameworkCore.SqlServer) Target framework: (e.g. .NET Core 3.0) Operating system: IDE: (e.g. Visual Studio 2019 16.3)

About this issue

  • Original URL
  • State: closed
  • Created 4 years ago
  • Reactions: 1
  • Comments: 24 (8 by maintainers)

Most upvoted comments

You are not correctly using async/await. To fix your issues:

Change the SetupViewBags signature from async void to async Task.

Then in your controller, properly await it and change the controller action signature as following:

public async Task<IActionResult> Create() 
{
   await SetupViewBags();
   return View();
}

Furthermore you should not block in GetNotificationsCount. Use await and don’t use .Result:

var userId = await GetCurrentTennantId();
return await _context.Notifications.Where(w => w.SharedTo == userId.ToString()).CountAsync();

Check your other code for these patterns and fix accordingly.

@tnlthanzeel I see. There are many technologies that work well with that approach, but since a DbContext only supports one call at a time, the recommended pattern is to just await multiple calls in sequence, instead of in parallel, because complexity can increase quickly, with more than one DbContext being involved. I would only go this road if necessary (e.g. due to performance issues) and even then only when other options are not more effective (e.g. a locally available read replica to get rid of latency). For more information, see Asynchronous Programming and its warning boxes in the EF Core docs.

(In my personal experience, simplicity is usually the greatest quality that source code can provide, because it positively effects the app in so many different ways. Once simplicity is gone, it will be much harder later to get it back again.)

Just to add to @lauxjpn great response above, if you generally want to keep DbContext as Scoped, you can still get injected with an IDbContextFactory in the specific place where you need multiple instances (see the docs). That would probably be a bit easier/cleaner than creating DI scopes yourself.

try inject multiple dbCOntexts maybe you can win this race condition issue. but watch out for DbUpdateException

each service has its own dbcontext injected in the ctor

try inject multiple dbCOntexts maybe you can win this race condition issue. but watch out for DbUpdateException

i found have a place where one of my team mate has added

services.AddScoped<GemStoContext, GemStoContext>();

so now i removed it and the Task.WhenAll() works only under transient ServiceLifetime. why does it not work in scoped ServiceLifetime as in ef core 2.1?

If you inject a DbContext into 3 different services, and your DbContext class was registered as scoped, then your 3 different services will get a reference to the same (single) DbContext object.

On the other hand, if you register your DbContext class as transient, your 3 different services will get a reference to 3 different DbContext instances. So it works because your services then don’t call into the same DbContext instance at the same time in parallel.

@tnlthanzeel If your code runs fine with the transient approach, then consider using it.

Another way to achieve the same would be to keep the DbContext scoped, but then create individual scopes for your services, so that each of your services ends up with their own distinct context again in the end. This makes sense, if you only want your 3 services to use different contexts, so they can run in parallel, but generally want to use the same DbContext instance in other places of your app.

I am curious what your reason for your optimization is:

  • Is the physical machine that runs the database at a different location than the web server (far away, e.g. half around the world) and accessing it results in a high overhead because of latency and your users have to wait a painfully long time for a web request to respond, so you want to solve the issue by executing as many database operations in parallel as possible, so the database server latency impacts you only once per web request?
  • The performance of a web request is fine, but you want to increase the web request throughput your application can handle on its current hardware?

(Because there might be better solutions to the underlying problem, depending on what it actually is.)

@tnlthanzeel what @lauxjpn writes in PomeloFoundation/Pomelo.EntityFrameworkCore.MySql#1023 is fully right - EF Core has never supported using the same DbContext instance concurrently from multiple threads. Some code using concurrent access may have accidentally worked in some situations, but only because it happened to not trigger an exception due to timing.

You’ll have to ensure that you’re not using the instance concurrently, either by serializing your queries, or by using multiple DbContext instances (which would represent multiple database connections).

If you’d like to pursue this further, we need a minimal, runnable code sample.

That is telling you that there are unnecessary requests that are been sent to the DbContext so use “await Task.Delay(3000);” about you code the is sending the request to the DbContext

I wanted to add that in my case I forgot to await _userManager.FunctionAsync(). So if you are having this error have a look around if you have put your await everywhere. 😉

@upizs If you are calling an async method without await in your code, the compiler should output a CS4014 warning in most of those cases, which is usually the quickest way to find the line responsible for the issue.

I wanted to add that in my case I forgot to await _userManager.FunctionAsync(). So if you are having this error have a look around if you have put your await everywhere. 😉

@tnlthanzeel I see. There are many technologies that work well with that approach, but since a DbContext only supports one call at a time, the recommended pattern is to just await multiple calls in sequence, instead of in parallel, because complexity can increase quickly, with more than one DbContext being involved. I would only go this road if necessary (e.g. due to performance issues) and even then only when other options are not more effective (e.g. a locally available read replica to get rid of latency). For more information, see Asynchronous Programming and its warning boxes in the EF Core docs.

(In my personal experience, simplicity is usually the greatest quality that source code can provide, because it positively effects the app in so many different ways. Once simplicity is gone, it will be much harder later to get it back again.)

@lauxjpn thanks for the advice. no i understand better. i never knew the fact we cannot use parallel programming on a dbcontext. just cam to know that just within 24 hours. thanks again sir

[…] if you generally want to keep DbContext as Scoped, you can still get injected with an IDbContextFactory in the specific place where you need multiple instances […]

@roji Nice one! I’ll keep that in mind for my own projects 😃

@andersonphiri Yes the problem is autofac. The problem was fixed when Autofac did InstancePerLifetimeScope () instead of SingleInstance (). Thanks for your answer.