GetRolesAsync 无痕迹地退出
Posted
技术标签:
【中文标题】GetRolesAsync 无痕迹地退出【英文标题】:GetRolesAsync exits without a trace 【发布时间】:2021-03-18 23:34:48 【问题描述】:这是一个 Blazor 服务器应用程序。我使用 Serilog,但没有显示任何信息。我希望某处会出现异常,但代码只是存在而没有踪迹。
这是我正在尝试做的简化版本。它在两个 GetRolesAsync 中的任何一个处随机退出:
// Both injected as Scoped at Startup.cs
private UserManager<IdentityUser> _userManager;
private RoleManager<IdentityRole> _roleManager;
public Task<IdentityUser> FetchIdentityUser()
// Two users:
var allUsers = _userManager.Users.ToList();
// Two roles:
var allRoles = _roleManager.Roles.ToList();
// Each user is attached to a single role in the db
// (checked via mysqlWorkbench)
var roleNames1 = _userManager.GetRolesAsync(allUsers.First()).Result; // <-- sometimes it exits here (no exception is thrown, no trace of what's going on ...)
var roleNames2 = _userManager.GetRolesAsync(allUsers.Last()).Result; // <-- ...and sometimes it exists here (no exception is thrown, no trace of what's goin on ...)
return Task.FromResult(allUsers.First());
【问题讨论】:
不应该FetchIdentityUser()
是异步的,然后你会 await
那两个 GetRolesAsync 调用?不确定,但这会出现在这里更有意义。
任何时候你发现自己在输入.Result
,你可能做错了非常非常错误(需要注意的是,对已经完成的任务进行微优化离开状态机; 如果你不知道确切这意味着什么:不要使用.Result
) - 你应该使用var roleName1 = await _userManager.GetRolesAsync(allUsers.First());
(roleNames2
类似) - 并将方法标记为@987654328 @
@MarcGravell 你是对的。通常我选择.Result
是因为“异步蠕变”(如果您不小心,它会迅速在您的代码中传播,最终导致一团糟)。例如:我已经以某种方式设计了一个代码,并且在我的代码中一直有一个等待的函数......感谢您的反馈(以及创建 Dapper!;)这使得 db 访问变得更简单和直观) .
【参考方案1】:
这些是异步方法,您需要等待它们。
编辑(感谢 Mark Gravell 的评论):您可能在这里看到的是同步上下文死锁,因为它们试图返回调用线程。这也可以更好地解释为什么您很难看到错误!
public **async** Task<IdentityUser> FetchIdentityUser()
// Two users:
var allUsers = _userManager.Users.ToList();
// Two roles:
var allRoles = _roleManager.Roles.ToList();
// Each user is attached to a single role in the db
// (checked via MysqlWorkbench)
var roleNames1 = await _userManager.GetRolesAsync(allUsers.First()); // <-- sometimes it exits here (no exception is thrown, no trace of what's going on ...)
var roleNames2 = await _userManager.GetRolesAsync(allUsers.Last()); // <-- ...and sometimes it exists here (no exception is thrown, no trace of what's goin on ...)
return Task.FromResult(allUsers.First());
您需要等待并将方法本身标记为异步(确保在调用它的地方等待它)。
在极少数情况下,您应该直接访问此类任务的结果,并且您需要明确结构化的代码才能使其工作。
【讨论】:
因为UserManager<TUser>.GetRolsAsync(TUser)
returns a task,这里不应该有竞争条件——它应该变成阻塞;它更有可能是由该阻塞引起的同步上下文死锁(请注意,您所描述的情况可能发生在涉及ValueTask<T>
的某些场景中,特别是在使用IValueTaskSource<T>
时)。不过,修复仍然是使用await
,所以:太好了!
是的,这确实解决了这个问题。问题是我帖子中的代码过于简化(尽管问题是相同的)。在我的原始代码中,我正在这样做:allUsers.Select(u => var roleNames = _userManager.GetRolesAsync(u).Result; ...
,我没有使用await
,因为我不知道我可以这样做:allUsers.Select(async u => var roleNames = await _userManager.GetRolesAsync(u); ...
,这解决了这个问题。这是我第一次使用 Identity,但由于“幕后”发生的所有事情,我发现它很难使用。以上是关于GetRolesAsync 无痕迹地退出的主要内容,如果未能解决你的问题,请参考以下文章