【发布时间】: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