EF. The connection was not closed. The connection's current state is connecting

20.8k Views Asked by At

I have jwt auth:

var messageHandlers = new JwtMessageHandler(_serviceProvider);

app.UseJwtBearerAuthentication(new JwtBearerOptions
{
    AutomaticAuthenticate = true,
    AutomaticChallenge = true,
    Events = new JwtBearerEvents
    {
        OnMessageReceived = messageHandlers.OnMessageReceived,
    },
    TokenValidationParameters = tokenValidationParameters
});

The JwtMessageHandler is my custom handler. In the handler I have to make some queries to database, so I pass ServiceProvider and resolve my user service:

public class JwtMessageHandler
{

        private IUserService _userService;  

        public async Task OnMessageReceived(MessageReceivedContext arg)
        {
             //parsing header, get claims from token
             ...
              _userService = (IUserService)arg.HttpContext.RequestServices.GetService(typeof(IUserService));
             var isRoleChanged = await _userService.IsRoleChanged(tokenObject.Subject, rolesFromToken);

            if (isRoleChanged)
            {
                GenerateBadResponse(arg);
                return;
            }

            var canLogin = await _userService.CanLogin(tokenObject.Subject);

            if (!canLogin)
            {
                GenerateBadResponse(arg);
                return;
            }
        }    
}

In the service I make queries:

...
 var user = await _userManager.FindByEmailAsync(email);
 var currentRoles = await _userManager.GetRolesAsync(user);
..

The OnMessageReceived is called for every request. When I have one request on page to the server or I wait one-two seconds before doing something all works fine. But, I have several pages where I make 2-3 simultaneous requests to the server. And, in this case I get error about:

The connection was not closed. The connection's current state is connecting

I understand that problem with multithreading. The JwtMessageHandler is created once when application is started. So, I put the line:

_userService = (IUserService)_serviceProvider.GetService(typeof(IUserService)); 

inside method, before it was located in the constructor. But, It didn't help. Also, I tried to set null to _userService in the end of my method.

How to correctly use in this case?

4

There are 4 best solutions below

2
On BEST ANSWER

Trying to use a connection that is already "connecting" - clear sign of some race condition.

  1. Re-check that IUserService is registered with "scope" lifetime, and all it dependencies (userManager, dbContext) too
  2. Do not use IServiceProvider you obtained during app startup for scope-bases services resolution - it is NOT related to current request scope and return instances from "some other universe". Use HttpContext.RequestServices for service resolution.
  3. Check that your are "awaiting" all async methods. If you start second request while still executing first one - you may possibly "catch" dbContext during "connecting" stage.
  4. Your JwtMessageHandler instance is one/single per app. So don't use it's property for storing _userService (remove private IUserService _userService). Instead, use local variable inside OnMessageReceived (var _userService = ...).

You already checked (1), (2) and (3). I think (4) is the last one you need to fix your bug.

1
On

@Dmitry's answer pointed me in the right direction. For me, I was having this issue in a MiddleWare on .NETCORE. What solved this issue for me is resolving the IUnitOfWork interface in the Invoke method of my Middleware.

I did something like

public Task Invoke(HttpContext context)
{
    _unitOfWork = (IUnitOfWork)context.RequestServices.GetService(typeof(IUnitOfWork));

       //Some checks            

        var apiKey = context.Request.Headers["X-ApiKey"][0];

        var clientApp = _unitOfWork.ClientApplicationsRepository.Items.FirstOrDefault(s => s.ApiKey == apiKey);

     //Some other code...

    return _next(context);
}
0
On

I have faced this situation a lot. I have always gotten by by using lock keyword.

lock (_context)
{
      var user = _context.users.first(x => x.Id == userId);
}

This locks the use of the object (i.e. _context) for the current thread and no other thread simultaneously can access this same instance hence no problems whatsoever.

0
On

In my case I was trying to use Managed Identity and constructed a SqlConnection in ConfigureServices and did AddDbContext<..>(o => o.UseSqlServer(sqlConnection);

That resulted in strange behaviour.

Fixed it by moving the code to the Context OnConfiguring

        var sqlConnection = new SqlConnection(Startup.Configuration.GetConnectionString("xxx"));
        optionsBuilder.UseSqlServer(sqlConnection);