#c# #asp.net-core #asp.net-identity #blazor #blazor-server-side
#c# #asp.net-ядро #asp.net-идентификация #блейзор #blazor-на стороне сервера
Вопрос:
Это серверное приложение 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()).Resu< // <-- sometimes it exits here (no exception is thrown, no trace of what's going on ...)
var roleNames2 = _userManager.GetRolesAsync(allUsers.Last()).Resu< // <-- ...and sometimes it exists here (no exception is thrown, no trace of what's goin on ...)
return Task.FromResult(allUsers.First());
}
Комментарии:
1. Не
FetchIdentityUser()
должно быть асинхронным, и тогда вы быawait
эти два GetRolesAsync вызывали? Не уверен, но, по- видимому , здесь это имеет немного больше смысла.2. каждый раз, когда вы обнаруживаете, что печатаете
.Result
, вы, вероятно, делаете что-то очень и очень неправильно (с оговоркой о микрооптимизации конечных автоматов для уже выполненных задач; если вы точно не знаете, что это значит: не используйте.Result
) — вы должны использоватьvar roleName1 = await _userManager.GetRolesAsync(allUsers.First());
(и аналогичные дляroleNames2
) — и отметьте метод какasync
3. @MarcGravell вы правы. Обычно, когда я выбираю, это происходит из-
.Result
за «асинхронной ползучести» (если вы не будете осторожны, она быстро распространяется по вашему коду, и в итоге получается беспорядок). например: я разработал код определенным образом, и на всем пути моего кода есть одна единственная функция, котораяожидаемый… Спасибо за отзыв (и за создание Dapper! 😉 что делает доступ к БД намного проще и интуитивно понятным).
Ответ №1:
Это асинхронные методы, и вам нужно их дождаться.
Редактировать (спасибо Марку Гравеллу за комментарий): то, что вы, вероятно, видите здесь, — это взаимоблокировка контекста синхронизации, поскольку они пытаются вернуться к вызывающему потоку. Это также намного лучше объяснило бы, почему вам трудно увидеть ошибку!
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());
}
Вам нужно ожидание и пометить сам метод как асинхронный (гарантируя, что он будет ожидаться там, откуда он вызывается).
Существует очень мало случаев, когда вам следует напрямую обращаться к результату подобной задачи, и вам потребуется, чтобы ваш код был явно структурирован, чтобы это работало.
Комментарии:
1.поскольку
UserManager<TUser>.GetRolsAsync(TUser)
возвращает задачу, здесь не должно быть условия гонки — оно должно стать блокировкой; скорее всего, это будет взаимоблокировка контекста синхронизации, вызванная этой блокировкой (обратите внимание, что то, что вы описываете, может произойти в некоторых сценариях, связанныхValueTask<T>
, в частности, при использованииIValueTaskSource<T>
). Однако исправление все еще нужно использоватьawait
, так что: отлично!2. Да, это действительно решило проблему. Дело в том, что код в моем сообщении был чрезмерно упрощен (хотя проблема была идентичной). В моем исходном коде я делал это:
allUsers.Select(u => { var roleNames = _userManager.GetRolesAsync(u).Resu< ...
, и я не использовалawait
, потому что не знал, что могу сделать это вместо:allUsers.Select(async u => { var roleNames = await _userManager.GetRolesAsync(u); ...
, что решило проблему. Я впервые использую Identity, и мне трудно использовать его из-за всех вещей, которые происходят «под капотом».