GetRolesAsync завершается без следа

#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, и мне трудно использовать его из-за всех вещей, которые происходят «под капотом».