Search code examples
postgresqlasp.net-coreasync-await.net-corenpgsql

An operation is already in progress error while accessing DB from middleware


Hello,

I have an ASP.NET Core app with EF Core running on postgresql db server. I ran into this problem:

When I access DB from a controller, everything works fine. But when I try to access DB from a middleware, an exception occurs (both under higher request load):

System.InvalidOperationException: An operation is already in progress.
   at Npgsql.NpgsqlConnector.StartUserAction(ConnectorState newState)
   at Npgsql.NpgsqlCommand.<ExecuteDbDataReaderInternalAsync>d__4.MoveNext()
...

Sample code:


DB configuration

private void ConfigureDatabase(IServiceCollection services)
{
    var connectionString = ConfigObject.Value.DbContextSettings.ConnectionString;
    services.AddDbContext<ApplicationDbContext>(opts => opts.UseNpgsql(connectionString));
}

DbContext

public class ApplicationDbContext : IdentityDbContext<ApplicationUser, Role, Guid>
  {

        ...

        public DbSet<SomeItem> SomeItems { get; set; }

        ...
  }

The middleware

public async Task Invoke(HttpContext context)  
        {
                var test = await _context.SomeItems.FirstOrDefaultAsync();
        }

An controller

public async Task<SomeItem> GetStuff()  
        {
                return await _context.SomeItems.FirstOrDefaultAsync();
        }

My question is - Why is the controller able to serve many async concurrent request and the middleware not? And how can I get it work too? I'm little bit lost in asynchronicity and ASP.NET anatomy right now.

Thank you in advance.


Solution

  • When you use constructor injection in middleware, if dependency lifetime is scoped it causes captive dependency. You used ApplicationDbContext and it is scoped. So the problem probably occurs from this reason. To avoid, you can pass dependency into Invoke method as a parameter:

        public async Task Invoke(HttpContext context, ApplicationDbContext _context)  
        {
             var test = await _context.SomeItems.FirstOrDefaultAsync();
        }