【问题标题】:UserManager error "A second operation started on this context before a previous operation completed"UserManager 错误“在前一个操作完成之前在此上下文上启动了第二个操作”
【发布时间】:2020-06-05 20:57:26
【问题描述】:

在我的 Blazor 服务器端应用程序中,我有多个组件,每个组件都通过依赖注入使用 UserManager,这些组件通常呈现在同一页面上。例如,我使用 NavMenu 中的 UserManager 向用户显示/隐藏某些导航项,然后在页面本身内设置逻辑以防止导航到页面本身中的相同页面。通常在导航到具有此逻辑的页面时,NavMenu 和 Page UserManager 似乎会发生冲突,从而导致错误:

InvalidOperationException: 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.

我确信这是其他人遇到的问题,但无法找到解决方案。如果我在包含多个带有注入 UserManager 的组件的页面上点击刷新,则最常发生这种情况。我很感激可以提供的任何帮助,如果需要,可以提供更多信息!

根据要求,这是我对 UserManager 的注册。 ApplicationUserManager 目前实际上并没有覆盖 UserManager 中的任何功能,只是为未来的定制/增强而实现:

            services.AddIdentity<WS7.Engine.Models.Identity.ApplicationUser, WS7.Engine.Models.Identity.ApplicationRole>(options =>
            {
                options.SignIn.RequireConfirmedAccount = true;
                options.User.RequireUniqueEmail = true;

                options.Password.RequireDigit = true;
                options.Password.RequireLowercase = true;
                options.Password.RequireNonAlphanumeric = true;
                options.Password.RequireUppercase = true;
                options.Password.RequiredLength = 6;
                options.Password.RequiredUniqueChars = 1;

            })
            .AddEntityFrameworkStores<WS7.Areas.Identity.Data.ApplicationIdentityContext>()
            .AddUserManager<ApplicationUserManager>()
            .AddSignInManager<ApplicationSignInManager>()
            .AddRoles<ApplicationRole>()
            .AddDefaultTokenProviders();

可能值得注意的是,似乎出现此错误的调​​用(基于堆栈跟踪)都在所涉及的各个组件的 OnInitializedAsync() 方法中。

两个组件的示例: 组件 1:

 protected override async Task OnInitializedAsync()
    {
        await base.OnInitializedAsync();
        await Authorize();
    }

    private async Task Authorize()
    {
        bool allowNavigate = (AllowedRoleIds.Count() == 0);
        var contextUser = _AuthorizeHttpContextAccessor.HttpContext.User;
        if (contextUser != null)
        {
            var user = await _AuthorizeUserManager.GetUserAsync(contextUser);
            if (user != null)
            {
                var result = await _AuthorizeIdentityService.GetUserRightsAsync(new Engine.Models.GetUserRightsParams()
                {
                    UserID = user.Id
                });
                if (result.Succeeded == Engine.Models.Base.SuccessState.Succeeded)
                {
                    if (result.UserRightIDs.Any(uri => AllowedRoleIds.Split(",").Any(ari => ari.Equals(uri, StringComparison.CurrentCultureIgnoreCase))))
                    {
                        allowNavigate = true;
                    }
                }
            }
        }
        if (allowNavigate == false)
        {
            _AuthorizeNavigationManager.NavigateTo("/Identity/Account/Login");
        }
    }

组件 2:

 protected override async Task OnInitializedAsync()
    {
        await RefreshData();
        await base.OnInitializedAsync();
    }

    private async Task RefreshData()
    {
        var userAccountsResult = await _IdentityService.GetAspNetUserAccountsAsync(new Engine.Models.GetAspNetUserAccountsParams()
        {
            //Return all. Don't set any Params
        });
        if (userAccountsResult.Succeeded == SuccessState.Succeeded)
        {
            var users = await _userManager.Users.ToListAsync();
            var usersView = users.Select(u => new UserViewModel()
            {
                Id = u.Id,
                UserName = u.UserName,
                FirstName = u.FirstName,
                LastName = u.LastName,
                Email = u.Email,
                AccountStatus = u.ApprovedStatus,
                EmailConfirmed = u.EmailConfirmed,
                Active = !u.InActive,
                UserAccounts = userAccountsResult.UserAccounts.Where(ua => ua.UserID == u.Id).Select(ua => new UserAccountModel()
                {
                    Account = ua.Account
                }).ToList()
            }).ToList();

            Users = usersView;
            FilteredUsers = usersView;
        }
        else
        {
            _StatusService.SetPageStatusMessage(new PageStatusMessageEventArgs()
            {
                AlertType = AlertType.Danger,
                Message = "There was an issue initializing the page."
            });

        }

    }

一个示例异常的堆栈跟踪:

System.InvalidOperationException: 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. at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection() at Microsoft.EntityFrameworkCore.Query.RelationalShapedQueryCompilingExpressionVisitor.AsyncQueryingEnumerable`1.AsyncEnumerator.MoveNextAsync() at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Query.ShapedQueryCompilingExpressionVisitor.SingleOrDefaultAsync[TSource](IAsyncEnumerable`1 asyncEnumerable, CancellationToken cancellationToken) at WS7.Areas.Identity.Data.ApplicationUserManager.FindByIdAsync(String userId) in C:\VB6\Web\WS7\WS7\Areas\Identity\Data\ApplicationUserManager.cs:line 31 at WS7.Areas.Identity.Data.ApplicationUserManager.GetUserAsync(ClaimsPrincipal principal) in C:\VB6\Web\WS7\WS7\Areas\Identity\Data\ApplicationUserManager.cs:line 35 at WS7.Components.PDAuthorizeBase.Authorize() in C:\VB6\Web\WS7\WS7\Components\PDAuthorizeBase.cs:line 51 at WS7.Components.PDAuthorizeBase.OnInitializedAsync() in C:\VB6\Web\WS7\WS7\Components\PDAuthorizeBase.cs:line 35

【问题讨论】:

  • @HenkHolterman 我已将我的 UserManager 注册码添加到主帖中。此外,我不相信我有任何遗漏的等待或任何异步无效,但我会重新审查我的项目。我在 Visual Studio 的“错误列表”窗口中没有显示任何警告。
  • @HenkHolterman 我有两个碰撞组件的示例。谢谢!
  • @HenkHolterman 另外,澄清一下,_IdentityService 对象是一项服务,它通过 WebApi 执行某些身份相邻操作 - 用户管理器无法完成的数据检索/操作(例如自定义表管理)
  • @HenkHolterman 我在帖子末尾添加了一个堆栈跟踪。谢谢!
  • UserManager 是 WS7 项目中唯一可访问的 DBContext;其他一切目前都通过 WebAPI 在一个单独的项目中处理。 UserManager 不总是单例吗?有没有办法以其他方式声明它?由于异步版本不接受 ClaimsPrincipal 对象,因此我对 GetUserID 或 GetUserName 非异步进行了几次调用-这些可能相关吗?我在帖子中添加了它,但这主要发生在我刷新页面并创建与服务器的新连接时。

标签: c# asp.net asp.net-identity blazor


【解决方案1】:

我认为您可能会在这里找到解决问题的方法:

在这些情况下,您应该使用OwningComponentBase&lt;T&gt;。下面是一个 显示正确模式的更新示例。

Read thisSee also this...

您是否在 Server Blazor 应用程序中使用 HttpContext ?如果你这样做,你不应该, 因为 Server Blazor App 不是基于 HTTP 的应用程序,而是基于 WebSocket 的应用程序。

希望这会有所帮助...

【讨论】:

  • 如果我理解正确,根据链接中提供的示例项目,将我的组件切换到 OwningComponentBase 并注入 UserManager 应该可以解决此问题?我尝试了这个但没有成功。另外,我目前正在通过 IHttpContextAccessor 获取 HTTPContext。使用 AuthenticationStateProvider 的方法是否正确?
  • 抱歉,由于我现在比较忙,所以现在我无法帮助您解决主要问题。但是,我建议您更改 DBContext 的范围。以下链接是我的帖子如何在最初访问您的应用程序时访问 HttpContext,在建立 WebSocket 连接之前,这是 HttpContext 唯一可用的时间。关于您关于 AuthenticationStateProvider 的问题,答案是肯定的。您必须非常认真地学习如何使用此服务和其他相关组件。这就是 Blazor 的发展方向。
  • 感谢 AuthenticationStateProvider 的提示!有没有办法改变 UserManager 中 DBContext 的范围?到目前为止,我试图这样做但没有成功。
猜你喜欢
  • 2014-12-08
  • 1970-01-01
  • 1970-01-01
  • 2019-12-17
  • 1970-01-01
  • 2022-01-01
  • 2020-01-22
  • 1970-01-01
  • 2021-05-04
相关资源
最近更新 更多