gpt4 book ai didi

c# - 在 UserManager 上等待导致错误在上一个操作完成之前在此上下文上启动了第二个操作

转载 作者:行者123 更新时间:2023-11-30 21:38:43 27 4
gpt4 key购买 nike

背景

我正在使用 .Net Core API 来驱动歌词应用程序。用户可以注册、提交艺术家和歌词,并在此过程中获得荣誉/XP 积分。基本上是一个社区驱动的歌词网站。

代码

这是我的 ArtistController 类:

[Route("api/artists")]
public class ArtistsController : Controller
{
private readonly IPermissionsService _permissionsService;
private readonly IArtistsService _artistsService;

public ArtistsController(IArtistsService artistsService, IPermissionsService permissionsService)
{
_permissionsService = permissionsService ?? throw new ArgumentNullException(nameof(permissionsService));
_artistsService = artistsService ?? throw new ArgumentNullException(nameof(artistsService));
}

[HttpGet("{slug}")]
[HttpGet("{slug}/lyrics", Name = "GetArtist")]
public async Task<IActionResult> GetArtist(string slug)
{
if (!_artistsService.ArtistExists(slug)) return NotFound();
var permissions = await _permissionsService.GetPermissions(HttpContext);
var artist = _artistsService.GetArtistBySlug(slug, permissions.UserId, permissions.IsAdministrator);
if (artist == null) return NotFound();
return Ok(artist);
}

// other methods omitted
}

本着可测试性的精神,我创建了一个IPermissionsService,这样当我对 Controller 进行单元测试时,我可以很容易地这样做而不用担心HttpContext用户

这是 PermissionsService 类的代码:

public class PermissionsService : IPermissionsService
{
private string _userId;
private bool _isAdministrator;
private HttpContext _httpContext;
private readonly UserManager<BbUser> _userManager;

public PermissionsService(UserManager<BbUser> userManager)
{
_userManager = userManager;
}

public async Task<Permissions> GetPermissions(HttpContext httpContext)
{
_httpContext = httpContext;
PopulateUserIdAndIsAdminFlag();
var permissions = new Permissions
{
UserId = _userId,
IsAdministrator = _isAdministrator
};

return await Task.Run(() => permissions);
}

private async void PopulateUserIdAndIsAdminFlag()
{
if (!IsAuthenticated()) return;
var username = _httpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await _userManager.FindByNameAsync(username);
var roles = await _userManager.GetRolesAsync(user);
_userId = user.Id;
_isAdministrator = roles.Contains("Admin");
}

private bool IsAuthenticated()
{
return _httpContext.User.Identity.IsAuthenticated;
}
}

问题

当我运行 API 并尝试调用该端点时。我收到以下错误:

A second operation started on this context before a previous operation completed. Any instance members are not guaranteed to be thread safe.

消息很清楚,但我不确定如何克服这个错误。在将该逻辑移出到 PermissionsService 之前,我没有收到错误并且一切正常!

最佳答案

执行 async 工作的方法应该返回 Task 而不是 void 除非它们是事件处理程序。这将允许等待生成的 Task。因为没有等待 PopulateUserIdAndIsAdminFlag,所以您正在跨线程同时调用同一个 DbContext 实例。如果您遵循调用堆栈:

  1. 代码进入GetPermissions
  2. 您在 PopulateUserIdAndIsAdminFlag 中开始工作 但不要等待它完成
  3. 代码立即从 GetPermissions 返回(PopulateUserIdAndIsAdminFlag 中的代码也仍在执行)
  4. 代码继续并调用 _artistsService 上的方法

这可能会导致多个线程同时调用 DbContext,从而导致异常。

修复代码以便等待 PopulateUserIdAndIsAdminFlag 的结果。

  • 更改您的代码以等待 PopulateUserIdAndIsAdminFlag 方法,以便它返回类型 Task
  • 等待 PopulateUserIdAndIsAdminFlag 的结果
  • GetPermissions 结束时不再需要将结果包装在 Task
  • 我还建议重命名它并添加后缀 Async,因为这被认为是返回类型 Task 的方法的正确命名约定。这将导致名为 GetPermissionsAsyncPopulateUserIdAndIsAdminFlagAsync
  • 的方法

更改代码:

public async Task<Permissions> GetPermissions(HttpContext httpContext)
{
_httpContext = httpContext;
// await result
await PopulateUserIdAndIsAdminFlag();
var permissions = new Permissions
{
UserId = _userId,
IsAdministrator = _isAdministrator
};

// wrapping the result in Task is no longer necessary
return permissions;
}

// change void to Task
private async Task PopulateUserIdAndIsAdminFlag()
{
if (!IsAuthenticated()) return;
var username = _httpContext.User.FindFirstValue(ClaimTypes.NameIdentifier);
var user = await _userManager.FindByNameAsync(username);
var roles = await _userManager.GetRolesAsync(user);
_userId = user.Id;
_isAdministrator = roles.Contains("Admin");
}

关于c# - 在 UserManager 上等待导致错误在上一个操作完成之前在此上下文上启动了第二个操作,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45736560/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com