Entity Framework Core: A second operation started on this context before a previous operation completed

C#Entity Frameworkasp.net Web-Api

C# Problem Overview


I'm working on a ASP.Net Core 2.0 project using Entity Framework Core

<PackageReference Include="Microsoft.EntityFrameworkCore" Version="2.0.1" />
  <PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="2.0.0" PrivateAssets="All" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="2.0.0"/>

And in one of my list methods I'm getting this error:

InvalidOperationException: A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.
Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()

This is my method:

    [HttpGet("{currentPage}/{pageSize}/")]
    [HttpGet("{currentPage}/{pageSize}/{search}")]
    public ListResponseVM<ClientVM> GetClients([FromRoute] int currentPage, int pageSize, string search)
    {
        var resp = new ListResponseVM<ClientVM>();
        var items = _context.Clients
            .Include(i => i.Contacts)
            .Include(i => i.Addresses)
            .Include("ClientObjectives.Objective")
            .Include(i => i.Urls)
            .Include(i => i.Users)
            .Where(p => string.IsNullOrEmpty(search) || p.CompanyName.Contains(search))
            .OrderBy(p => p.CompanyName)
            .ToPagedList(pageSize, currentPage);

        resp.NumberOfPages = items.TotalPage;

        foreach (var item in items)
        {
            var client = _mapper.Map<ClientVM>(item);

            client.Addresses = new List<AddressVM>();
            foreach (var addr in item.Addresses)
            {
                var address = _mapper.Map<AddressVM>(addr);
                address.CountryCode = addr.CountryId;
                client.Addresses.Add(address);
            }

            client.Contacts = item.Contacts.Select(p => _mapper.Map<ContactVM>(p)).ToList();
            client.Urls = item.Urls.Select(p => _mapper.Map<ClientUrlVM>(p)).ToList();
            client.Objectives = item.Objectives.Select(p => _mapper.Map<ObjectiveVM>(p)).ToList();
            resp.Items.Add(client);
        }

        return resp;
    }

I'm a bit lost especially because it works when I run it locally, but when I deploy to my staging server (IIS 8.5) it gets me this error and it was working normally. The error started to appear after I increase the max length of one of my models. I also updated the max length of the corresponding View Model. And there are many other list methods that are very similar and they are working.

I had a Hangfire job running, but this job doesn't use the same entity. That's all I can think to be relevant. Any ideas of what could be causing this?

C# Solutions


Solution 1 - C#

I am not sure if you are using IoC and Dependency Injection to resolve your DbContext where ever it might be used. If you do and you are using native IoC from .NET Core (or any other IoC-Container) and you are getting this error, make sure to register your DbContext as Transient. Do

services.AddDbContext<MyContext>(ServiceLifetime.Transient);

OR

services.AddTransient<MyContext>();

instead of

services.AddDbContext<MyContext>();

AddDbContext adds the context as scoped, which might cause troubles when working with multiple threads.

Also async / await operations can cause this behaviour, when using async lambda expressions.

Adding it as transient also has its downsides. You will not be able to make changes to some entity over multiple classes that are using the context because each class will get its own instance of your DbContext.

The simple explanation for that is, that the DbContext implementation is not thread-safe. You can read more about this here

Solution 2 - C#

In some cases, this error occurs when calling an async method without the await keyword, which can simply be solved by adding await before the method call. however, the answer might not be related to the mentioned question but it can help solving a similar error.

Solution 3 - C#

The exception means that _context is being used by two threads at the same time; either two threads in the same request, or by two requests.

Is your _context declared static maybe? It should not be.

Or are you calling GetClients multiple times in the same request from somewhere else in your code?

You may already be doing this, but ideally, you'd be using dependency injection for your DbContext, which means you'll be using AddDbContext() in your Startup.cs, and your controller constructor will look something like this:

private readonly MyDbContext _context; //not static

public MyController(MyDbContext context) {
    _context = context;
}

If your code is not like this, show us and maybe we can help further.

Solution 4 - C#

I had the same problem and it turned out that parent service was a singelton. So the context automatically became singelton too. Even though was declared as Per Life Time Scoped in DI.

Injecting service with different lifetimes into another

  1. Never inject Scoped & Transient services into Singleton service. ( This effectively converts the transient or scoped service into the singleton. )

  2. Never inject Transient services into scoped service ( This converts the transient service into the scoped. )

Solution 5 - C#

  • Solve my problem using this line of code in my Startup.cs file.
    Adding a transient service means that each time the service is requested, a new instance is created when you are working with Dependency injection

            services.AddDbContext<Context>(options =>
                             options.UseSqlServer(_configuration.GetConnectionString("ContextConn")),
                  ServiceLifetime.Transient);
    

Solution 6 - C#

I think this answer still can help some one and save many times. I solved a similar issue by changing IQueryable to List(or to array, collection...).

For example:

var list = _context.table1.Where(...);

to

var list = _context.table1.Where(...).ToList(); //or ToArray()...

Solution 7 - C#

I had the same error. It happened because I called a method that was constructed as public async void ... instead of public async Task ....

Solution 8 - C#

Entity Framework Core does not support multiple parallel operations being run on the same DbContext instance. This includes both parallel execution of async queries and any explicit concurrent use from multiple threads. Therefore, always await async calls immediately, or use separate DbContext instances for operations that execute in parallel.

Solution 9 - C#

I faced the same issue but the reason was none of the ones listed above. I created a task, created a scope inside the task and asked the container to obtain a service. That worked fine but then I used a second service inside the task and I forgot to also asked for it to the new scope. Because of that, the 2nd service was using a DbContext that was already disposed.

Task task = Task.Run(() =>
    {
        using (var scope = serviceScopeFactory.CreateScope())
        {
            var otherOfferService = scope.ServiceProvider.GetService<IOfferService>();
            // everything was ok here. then I did: 
            productService.DoSomething(); // (from the main scope) and this failed because the db context associated to that service was already disposed.
            ...
        }
    }

I should have done this:

var otherProductService = scope.ServiceProvider.GetService<IProductService>();
otherProductService.DoSomething();

Solution 10 - C#

I have a background service that performs an action for each entry in a table. The problem is, that if I iterate over and modify some data all on the same instance of the DbContext this error occurs.

One solution, as mentioned in this thread is to change the DbContext's lifetime to transient by defining it like

services.AddDbContext<DbContext>(ServiceLifetime.Transient);

but because I do changes in multiple different services and commit them at once using the SaveChanges() method this solution doesnt work in my case.

Because my code runs in a service, I was doing something like

using (var scope = Services.CreateScope())
{
   var entities = scope.ServiceProvider.GetRequiredService<IReadService>().GetEntities();
   var writeService = scope.ServiceProvider.GetRequiredService<IWriteService>();
   foreach (Entity entity in entities)
   {
       writeService.DoSomething(entity);
   } 
}

to be able to use the service like if it was a simple request. So to solve the issue i just split the single scope into two, one for the query and the other for the write operations like so:

using (var readScope = Services.CreateScope())
using (var writeScope = Services.CreateScope())
{
   var entities = readScope.ServiceProvider.GetRequiredService<IReadService>().GetEntities();
   var writeService = writeScope.ServiceProvider.GetRequiredService<IWriteService>();
   foreach (Entity entity in entities)
   {
       writeService.DoSomething(entity);
   } 
}

Like that, there are effevtively two different instances of the DbContext being used.

Another possible solution would be to make sure, that the read operation has terminated before starting the iteration. That is not very pratical in my case because there could be a lot of results that would all need to be loaded into memory for the operation which I tried to avoid by using a Queryable in the first place.

Solution 11 - C#

There is some function Async but you don't call await before it. Find and add await before calling that function will help you.

Solution 12 - C#

This error might also appear if you created the migration (Add-Migration) but forgot to actually update the database (Update-Database).

Solution 13 - C#

I know this issue has been asked two years ago, but I just had this issue and the fix I used really helped.

If you are doing two queries with the same Context - you might need to remove the AsNoTracking. If you do use AsNoTracking you are creating a new data-reader for each read. Two data readers cannot read the same data.

Solution 14 - C#

I got the same message. But it's not making any sense in my case. My issue is I used a "NotMapped" property by mistake. It probably only means an error of Linq syntax or model class in some cases. The error message seems misleading. The original meaning of this message is you can't call async on same dbcontext more than once in the same request.

[NotMapped]
public int PostId { get; set; }
public virtual Post Post { get; set; }

You can check this link for detail, https://www.softwareblogs.com/Posts/Details/5/error-a-second-operation-started-on-this-context-before-a-previous-operation-completed

Solution 15 - C#

I got the same problem when I try to use FirstOrDefaultAsync() in the async method in the code below. And when I fixed FirstOrDefault() - the problem was solved!

_context.Issues.Add(issue);
        await _context.SaveChangesAsync();

        int userId = _context.Users
            .Where(u => u.UserName == Options.UserName)
            .FirstOrDefaultAsync()
            .Id;
...

Solution 16 - C#

I managed to get that error by passing an IQueryable into a method that then used that IQueryable 'list' as part of a another query to the same context.

public void FirstMethod()
{
    // This is returning an IQueryable
    var stockItems = _dbContext.StockItems
        .Where(st => st.IsSomething);

    SecondMethod(stockItems);
}

public void SecondMethod(IEnumerable<Stock> stockItems)
{
    var grnTrans = _dbContext.InvoiceLines
        .Where(il => stockItems.Contains(il.StockItem))
        .ToList();
}

To stop that happening I used the approach here and materialised that list before passing it the second method, by changing the call to SecondMethod to be SecondMethod(stockItems.ToList()

Solution 17 - C#

My situation is different: I was trying to seed the database with 30 users, belonging to specific roles, so I was running this code:

for (var i = 1; i <= 30; i++)
{
    CreateUserWithRole("Analyst", $"analyst{i}", UserManager);
}

This was a Sync function. Inside of it I had 3 calls to:

UserManager.FindByNameAsync(username).Result
UserManager.CreateAsync(user, pass).Result
UserManager.AddToRoleAsync(user, roleName).Result

When I replaced .Result with .GetAwaiter().GetResult(), this error went away.

Solution 18 - C#

First, upvote (at the least) alsami's answer. That got me on the right path.

But for those of you doing IoC, here is a little bit of a deeper dive.

My error (same as others)

> One or more errors occurred. (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.)

My code setup. "Just the basics"...

public class MyCoolDbContext: DbContext{
    public DbSet <MySpecialObject> MySpecialObjects {        get;        set;    }
}

and

public interface IMySpecialObjectDomainData{}

and (note MyCoolDbContext is being injected)

public class MySpecialObjectEntityFrameworkDomainDataLayer: IMySpecialObjectDomainData{
    public MySpecialObjectEntityFrameworkDomainDataLayer(MyCoolDbContext context) {
        /* HERE IS WHERE TO SET THE BREAK POINT, HOW MANY TIMES IS THIS RUNNING??? */
        this.entityDbContext = context ?? throw new ArgumentNullException("MyCoolDbContext is null", (Exception)null);
    }
}

and

public interface IMySpecialObjectManager{}

and

public class MySpecialObjectManager: IMySpecialObjectManager
{
    public const string ErrorMessageIMySpecialObjectDomainDataIsNull = "IMySpecialObjectDomainData is null";
    private readonly IMySpecialObjectDomainData mySpecialObjectDomainData;

    public MySpecialObjectManager(IMySpecialObjectDomainData mySpecialObjectDomainData) {
        this.mySpecialObjectDomainData = mySpecialObjectDomainData ?? throw new ArgumentNullException(ErrorMessageIMySpecialObjectDomainDataIsNull, (Exception)null);
    }
}

And finally , my multi threaded class, being called from a Console App(Command Line Interface app)

    public interface IMySpecialObjectThatSpawnsThreads{}

and

public class MySpecialObjectThatSpawnsThreads: IMySpecialObjectThatSpawnsThreads
{
    public const string ErrorMessageIMySpecialObjectManagerIsNull = "IMySpecialObjectManager is null";

    private readonly IMySpecialObjectManager mySpecialObjectManager;

    public MySpecialObjectThatSpawnsThreads(IMySpecialObjectManager mySpecialObjectManager) {
        this.mySpecialObjectManager = mySpecialObjectManager ?? throw new ArgumentNullException(ErrorMessageIMySpecialObjectManagerIsNull, (Exception)null);
    }
}

and the DI buildup. (Again, this is for a console application (command line interface)...which exhibits slight different behavior than web-apps)

private static IServiceProvider BuildDi(IConfiguration configuration) {
    /* this is being called early inside my command line application ("console application") */

    string defaultConnectionStringValue = string.Empty; /* get this value from configuration */

    ////setup our DI
    IServiceCollection servColl = new ServiceCollection()
        ////.AddLogging(loggingBuilder => loggingBuilder.AddConsole())

        /* THE BELOW TWO ARE THE ONES THAT TRIPPED ME UP.  */
        .AddTransient<IMySpecialObjectDomainData, MySpecialObjectEntityFrameworkDomainDataLayer>()
    .AddTransient<IMySpecialObjectManager, MySpecialObjectManager>()

    /* so the "ServiceLifetime.Transient" below................is what you will find most commonly on the internet search results */
     # if (MY_ORACLE)
        .AddDbContext<ProvisioningDbContext>(options => options.UseOracle(defaultConnectionStringValue), ServiceLifetime.Transient);
     # endif

     # if (MY_SQL_SERVER)
        .AddDbContext<ProvisioningDbContext>(options => options.UseSqlServer(defaultConnectionStringValue), ServiceLifetime.Transient);
     # endif

    servColl.AddSingleton <IMySpecialObjectThatSpawnsThreads,        MySpecialObjectThatSpawnsThreads>();

    ServiceProvider servProv = servColl.BuildServiceProvider();

    return servProv;
}

The ones that surprised me were the (change to) transient for

        .AddTransient<IMySpecialObjectDomainData, MySpecialObjectEntityFrameworkDomainDataLayer>()
    .AddTransient<IMySpecialObjectManager, MySpecialObjectManager>()

Note, I think because IMySpecialObjectManager was being injected into "MySpecialObjectThatSpawnsThreads", those injected objects needed to be Transient to complete the chain.

The point being.......it wasn't just the (My)DbContext that needed .Transient...but a bigger chunk of the DI Graph.

Debugging Tip:

This line:

this.entityDbContext = context ?? throw new ArgumentNullException("MyCoolDbContext is null", (Exception)null);

Put your debugger break point there. If your MySpecialObjectThatSpawnsThreads is making N number of threads (say 10 threads for example)......and that line is only being hit once...that's your issue. Your DbContext is crossing threads.

BONUS:

I would suggest reading this below url/article (oldie but goodie) about the differences web-apps and console-apps

https://mehdi.me/ambient-dbcontext-in-ef6/

Here is the header of the article in case the link changes.

> MANAGING DBCONTEXT THE RIGHT WAY WITH ENTITY FRAMEWORK 6: AN IN-DEPTH > GUIDE Mehdi El Gueddari

I hit this issue with WorkFlowCore https://github.com/danielgerlag/workflow-core

  <ItemGroup>
    <PackageReference Include="WorkflowCore" Version="3.1.5" />
  </ItemGroup>

sample code below.. to help future internet searchers

 namespace MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Workflows
    {
        using System;
        using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Constants;
        using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.MySpecialObjectInterview.Glue;
        using MyCompany.Proofs.WorkFlowCoreProof.BusinessLayer.Workflows.WorkflowSteps;
    
        using WorkflowCore.Interface;
        using WorkflowCore.Models;
    
        public class MySpecialObjectInterviewDefaultWorkflow : IWorkflow<MySpecialObjectInterviewPassThroughData>
        {
            public const string WorkFlowId = "MySpecialObjectInterviewWorkflowId";
    
            public const int WorkFlowVersion = 1;
    
            public string Id => WorkFlowId;
    
            public int Version => WorkFlowVersion;
    
            public void Build(IWorkflowBuilder<MySpecialObjectInterviewPassThroughData> builder)
            {
                builder
                             .StartWith(context =>
                    {
                        Console.WriteLine("Starting workflow...");
                        return ExecutionResult.Next();
                    })
    
    					/* bunch of other Steps here that were using IMySpecialObjectManager.. here is where my DbContext was getting cross-threaded */
     
    
                    .Then(lastContext =>
                    {
                        Console.WriteLine();
    
                        bool wroteConcreteMsg = false;
                        if (null != lastContext && null != lastContext.Workflow && null != lastContext.Workflow.Data)
                        {
                            MySpecialObjectInterviewPassThroughData castItem = lastContext.Workflow.Data as MySpecialObjectInterviewPassThroughData;
                            if (null != castItem)
                            {
                                Console.WriteLine("MySpecialObjectInterviewDefaultWorkflow complete :)  {0}   -> {1}", castItem.PropertyOne, castItem.PropertyTwo);
                                wroteConcreteMsg = true;
                            }
                        }
    
                        if (!wroteConcreteMsg)
                        {
                            Console.WriteLine("MySpecialObjectInterviewDefaultWorkflow complete (.Data did not cast)");
                        }
    
                        return ExecutionResult.Next();
                    }))
    
                    .OnError(WorkflowCore.Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(60));
    
            }
        }
    }

and

ICollection<string> workFlowGeneratedIds = new List<string>();
                for (int i = 0; i < 10; i++)
                {
                    MySpecialObjectInterviewPassThroughData currentMySpecialObjectInterviewPassThroughData = new MySpecialObjectInterviewPassThroughData();
                    currentMySpecialObjectInterviewPassThroughData.MySpecialObjectInterviewPassThroughDataSurrogateKey = i;

                    ////  private readonly IWorkflowHost workflowHost;
                    string wfid = await this.workflowHost.StartWorkflow(MySpecialObjectInterviewDefaultWorkflow.WorkFlowId, MySpecialObjectInterviewDefaultWorkflow.WorkFlowVersion, currentMySpecialObjectInterviewPassThroughData);
                    workFlowGeneratedIds.Add(wfid);
                }

Solution 19 - C#

In my case I use a template component in Blazor.

 <BTable ID="Table1" TotalRows="MyList.Count()">

The problem is calling a method (Count) in the component header. To resolve the problem I changed it like this :

int total = MyList.Count();

and later :

<BTable ID="Table1" TotalRows="total">

Solution 20 - C#

I had this issue before. The problem is the async/await operations. I made one function async. This function sets the conectionstring for the database on runtime. I made this function async function. So, I remove the async/await operation for this function, and the problem is solved. Making the database transient can solve the problem but it is bad if you need to update data on the database for many tables.

Solution 21 - C#

this happened to me . Because the method was async ,As I used await the problem solved.

Solution 22 - C#

Adding another possible resolution to this error, in case it helps someone.

In my case the issue was using a nav property inside a query, like this:

var selectedOrder = dbContext.Orders.Where(x => x.Id == id).Single();
var relatedOrders = dbContext.Orders.Where(x => x.User.Id == selectedOrder.User.Id).ToList();

The issue is using selectedOrder.User.Id in the query. If the User nav property hasn't already been loaded, EF will try to lazy load the property in the middle of trying to execute the query, which it sees as trying to start a second operation. The solution was to make a separate variable for selectedOrder.User.Id, to make sure the information needed for the query was loaded before the query started:

var selectedOrder = dbContext.Orders.Where(x => x.Id == id).Single();
var userId = selectedOrder.User.Id;
var relatedOrders = dbContext.Orders.Where(x => x.User.Id == userId).ToList();

Solution 23 - C#

In my case I was using a lock which does not allow the use of await and does not create compiler warning when you don't await an async.

The problem:

lock (someLockObject) {
    // do stuff
    context.SaveChangesAsync();
}

// some other code somewhere else doing await context.SaveChangesAsync() shortly after the lock gets the concurrency error

The fix: Wait for the async inside the lock by making it blocking with a .Wait()

lock (someLockObject) {
    // do stuff
    context.SaveChangesAsync().Wait();
}

Solution 24 - C#

Another possible case: if you use the connection direct, don't forget to close if. I needed to execute arbitrary SQL query, and read the result. This was a quick fix, I did not want to define a data class, not set up "normal" SQL connection. So simply I reused EFC's database connection as var connection = Context.Database.GetDbConnection() as SqlConnection. Make sure you call connection.Close() before you do Context.SaveChanges().

Solution 25 - C#

I tried all above solutions, none helped me the way I had my code running. Then adding Task.Delay(300).Wait(); before using the this._context... solved the issue as it waits for 300ms before accessing the context allowing previous operation to be completed.

Final code looks like this

Task.Delay(300).Wait();
response.Item = await this._context.Users
                 .Where(x => x.Username.ToLower().Equals(email))
                 .FirstOrDefaultAsync(); 

Solution 26 - C#

If your method is returning something back, you can solve this error by putting .Result to the end of the job and .Wait() if it doesn't return anything.

Solution 27 - C#

you can use SemaphoreSlim to block the next thread that will try to execute that EF call.

static SemaphoreSlim semSlim = new SemaphoreSlim(1, 1);

await semSlim.WaitAsync();
try
{
  // something like this here...
  // EmployeeService.GetList(); or...
  var result = await _ctx.Employees.ToListAsync();
}
finally
{
  semSlim.Release();
}

Solution 28 - C#

I just managed to make it work again. It makes not much sense but it worked:

  1. Remove Hangfire from StartUp (I was creating my job there)
  2. Deleted the hangfire database
  3. Restarted the server

I'll investigate further later but the method I called with hangfire receives a DBContext and that is the possible cause.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndr&#233; LuizView Question on Stackoverflow
Solution 1 - C#alsamiView Answer on Stackoverflow
Solution 2 - C#Hamid NasirlooView Answer on Stackoverflow
Solution 3 - C#Gabriel LuciView Answer on Stackoverflow
Solution 4 - C#DiPixView Answer on Stackoverflow
Solution 5 - C#Muhammad ArmaghanView Answer on Stackoverflow
Solution 6 - C#Makhidi MasimovView Answer on Stackoverflow
Solution 7 - C#RaynlazeView Answer on Stackoverflow
Solution 8 - C#Ehsan GondalView Answer on Stackoverflow
Solution 9 - C#Francisco GoldensteinView Answer on Stackoverflow
Solution 10 - C#NiPfiView Answer on Stackoverflow
Solution 11 - C#Lê Văn HiếuView Answer on Stackoverflow
Solution 12 - C#Luis GouveiaView Answer on Stackoverflow
Solution 13 - C#Cornelis de JagerView Answer on Stackoverflow
Solution 14 - C#Sharon ZhouView Answer on Stackoverflow
Solution 15 - C#JuSik404View Answer on Stackoverflow
Solution 16 - C#tomRedoxView Answer on Stackoverflow
Solution 17 - C#CatalinView Answer on Stackoverflow
Solution 18 - C#granadaCoderView Answer on Stackoverflow
Solution 19 - C#Ali BorjianView Answer on Stackoverflow
Solution 20 - C#Kyrolus Kamal FahimView Answer on Stackoverflow
Solution 21 - C#Ali MahdianView Answer on Stackoverflow
Solution 22 - C#Kestrel12View Answer on Stackoverflow
Solution 23 - C#TikallView Answer on Stackoverflow
Solution 24 - C#kleniumView Answer on Stackoverflow
Solution 25 - C#Amit SharmaView Answer on Stackoverflow
Solution 26 - C#Hasan_HView Answer on Stackoverflow
Solution 27 - C#aspnetcoreguyView Answer on Stackoverflow
Solution 28 - C#André LuizView Answer on Stackoverflow