Ошибка UserManager «Вторая операция началась в этом контексте до завершения предыдущей операции» - PullRequest
0 голосов
/ 21 февраля 2020

В моем приложении Blazor на стороне сервера у меня есть несколько компонентов, каждый из которых использует UserManager посредством внедрения зависимостей, которые часто отображаются на одной странице. Например, я использую UserManager в NavMenu, чтобы показать / скрыть определенные элементы навигации от пользователя, а затем добавить логики c на самих страницах, чтобы предотвратить переход к тем же страницам на самих страницах. Часто при переходе на страницу, имеющую этот лог c, NavMenu и Page UserManager Page сталкиваются, что приводит к ошибке:

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.

Я уверен, что это проблема, с которой столкнулись другие , но не смогли найти решение. Чаще всего это происходит, если я нажимаю Refre sh на странице, содержащей несколько компонентов, с введенным 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();

Возможно, стоит отметить, что вызовы, которые кажутся раздувающимися с этой ошибкой (на основе трассировки стека) ) оба входят в методы OnInitializedAsyn c () различных задействованных компонентов.

Пример двух компонентов: Компонент 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

1 Ответ

1 голос
/ 21 февраля 2020

Я думаю, вы можете найти решение вашей проблемы здесь:

В этих ситуациях вам следует использовать OwningComponentBase<T>. Ниже приведен обновленный образец, который показывает правильный шаблон.

Прочтите это . См. Также это ...

Используете ли вы HttpContext в приложении Server Blazor? Если вы это сделаете, вы не должны, так как приложение Server Blazor - это не приложения на основе HTTP, а приложения на основе WebSocket.

Надеюсь, это поможет ...

...