【问题标题】:EF. The connection was not closed. The connection's current state is connecting英孚。连接未关闭。连接的当前状态是正在连接
【发布时间】:2017-05-04 05:28:48
【问题描述】:

我有 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。 当我在页面上有一个对服务器的请求时,或者我在做某事之前等待一两秒钟,一切正常。但是,我有几个页面同时向服务器发出 2-3 个请求。而且,在这种情况下,我会收到以下错误:

连接没有关闭。连接的当前状态是正在连接

我理解多线程的问题。 JwtMessageHandler 在应用程序启动时创建一次。所以,我把这条线:

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

inside 方法,在它位于构造函数之前。但是,它没有帮助。另外,我尝试在方法结束时将 null 设置为 _userService

这种情况下如何正确使用?

【问题讨论】:

  • 您如何在Startup 中注册IUserService?单例、作用域、过渡?
  • @Dmitry 它的范围。 services.AddScoped<IUserService, UserService>();
  • 而您尝试使用在“当前”范围之外创建的serviceProvider 来获取范围服务......非常危险。查看内部MessageReceivedContext - 那里应该有其他ServiceProvider,直接或通过HttpContext...从那里询问您的服务。
  • @Dmitry 刚试过。 _userService = (IUserService)arg.HttpContext.RequestServices.GetService(typeof(IUserService));。而且,无论如何,我得到了错误。主要问题是捕获错误,我随机收到此错误。我可以工作一段时间,然后只有重新启动服务器才有帮助。
  • 对您的应用程序的每个请求都会创建单独的 HttpRequest 和单独的范围以及单独的 IUserService 实例,它们应该使用单独的 DbContext 实例。您的 DbContext 注册是否“正常”?你确定你在等待OnMessageReceived 本身吗?可能是请求处理链中的某些内容在您仍在执行IsRoleChanged 时对数据库进行查询?

标签: c# asp.net entity-framework asp.net-core entity-framework-core


【解决方案1】:

尝试使用已经“正在连接”的连接 - 明显存在某种竞争条件的迹象。

  1. 重新检查IUserService 是否注册了“范围”生命周期,以及它的所有依赖项(userManager、dbContext)
  2. 不要使用您在应用程序启动期间获得的IServiceProvider 用于基于范围的服务解析 - 它与当前请求范围无关,并从“其他宇宙”返回实例。使用HttpContext.RequestServices 进行服务解析。
  3. 检查您是否正在“等待”所有异步方法。如果您在执行第一个请求的同时开始第二个请求 - 您可能会在“连接”阶段“捕获”dbContext。
  4. 您的JwtMessageHandler 实例是每个应用一个/一个。所以不要使用它的属性来存储_userService(删除private IUserService _userService)。而是在 OnMessageReceived (var _userService = ...) 中使用局部变量。

您已经检查过 (1)、(2) 和 (3)。我认为 (4) 是您需要修复错误的最后一个。

【讨论】:

  • 我测试了 2 个小时,一切正常。谢谢你非常匹配。
  • @Dmitry,感谢您让我了解 Race Condition 是什么。
【解决方案2】:

@Dmitry 的回答为我指明了正确的方向。对我来说,我在 .NETCORE 的 MiddleWare 中遇到了这个问题。为我解决这个问题的方法是在我的中间件的 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);
}

【讨论】:

  • 这个答案缺乏太多的上下文,对任何人都没有帮助。读者可能会假设您的 IUnitOfWork 是由您的 DbContext 子类实现的接口,但您可能如何通过多个线程执行相同的实例尚不清楚。
【解决方案3】:

我经常遇到这种情况。 我一直使用 lock 关键字。

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

这为当前线程锁定了对象(即_context)的使用,并且没有其他线程可以同时访问同一个实例,因此没有任何问题。

【讨论】:

    【解决方案4】:

    在我的情况下,我尝试使用托管标识并在 ConfigureServices 中构造了一个 SqlConnection 并做了 AddDbContext<..>(o => o.UseSqlServer(sqlConnection);

    这导致了奇怪的行为。

    通过将代码移动到 Context OnConfiguring 来修复它

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-09-13
      • 1970-01-01
      • 2016-01-07
      相关资源
      最近更新 更多