web-dev-qa-db-ja.com

EF。接続は閉じられませんでした。接続の現在の状態は接続中です

私はjwt認証を持っています:

var messageHandlers = new JwtMessageHandler(_serviceProvider);

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

JwtMessageHandlerは私のカスタムハンドラーです。ハンドラーでデータベースにクエリを実行する必要があるため、ServiceProviderを渡して、ユーザーサービスを解決します。

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;
            }
        }    
}

サービスで私はクエリを行います:

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

OnMessageReceivedは、リクエストごとに呼び出されます。サーバーへのページに1つのリクエストがある場合、または1〜2秒待ってから何かを実行すると、すべて正常に機能します。しかし、サーバーに2〜3の同時リクエストを行うページがいくつかあります。そして、この場合、私は以下についてエラーを受け取ります:

接続は閉じられませんでした。接続の現在の状態は接続中です

マルチスレッドの問題を理解しています。 JwtMessageHandlerは、アプリケーションの起動時に1回作成されます。だから、私は次のように言います:

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

コンストラクターに配置される前のメソッド内。しかし、それは役に立ちませんでした。また、メソッドの最後でnullを_userServiceに設定しようとしました。

この場合、正しく使用するにはどうすればよいですか?

6
user348173

すでに「接続」している接続を使用しようとしています-競合状態の明らかな兆候です。

  1. IUserServiceが「スコープ」の有効期間に登録されていること、およびすべての依存関係(userManager、dbContext)も再確認してください
  2. アプリの起動時に取得したIServiceProviderをスコープベースのサービスの解決に使用しないでください。これは、現在のリクエストスコープとは関係がなく、「他のユニバース」からのインスタンスを返します。サービスの解決にはHttpContext.RequestServicesを使用します。
  3. すべての非同期メソッドを「待機」していることを確認してください。最初のリクエストの実行中に2番目のリクエストを開始すると、「接続」段階でdbContextを「キャッチ」する可能性があります。
  4. JwtMessageHandlerインスタンスはアプリごとに1つ/単一です。したがって、_userServiceを格納するためにそのプロパティを使用しないでください(private IUserService _userServiceを削除してください)。代わりに、OnMessageReceivedvar _userService = ...)内のローカル変数を使用してください。

すでに(1)、(2)、(3)をチェックしました。 (4)はバグを修正するために必要な最後のものだと思います。

12
Dmitry

@Dmitryの答えは私を正しい方向に向けました。私の場合、この問題は.NETCOREのミドルウェアで発生していました。私にとってこの問題を解決したのは、ミドルウェアのInvokeメソッドのIUnitOfWorkインターフェイスを解決することです。

私は次のようなことをしました

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);
}

私はこの状況に何度も直面しました。私はいつもlockキーワードを使ってやってきた。

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

これにより、現在のスレッドに対するオブジェクト(つまり、_context)の使用がロックされ、他のスレッドがこの同じインスタンスに同時にアクセスすることはできないため、問題は発生しません。

0
user3836777