我正在使用实体框架来处理 .NET 应用程序中的用户。我现在遇到的问题是,如果多个用户在同一时间发出相同的请求,服务器将错误的数据返回给用户,他们就会混淆。
根据我的阅读,这很可能是并发问题,但我还没有找到正确的解决方案。我之前添加了日志记录只是为了查看导致问题的原因,如果我同时在此端点发出 2 个请求,则不会有 2 个具有 2 个不同用户 ID 的日志,而是仅保存 1 个日志,而且用户也是混合的又起来了。
解决这个问题的正确方法是什么?甚至尝试过使用悲观并发的 chatgpts,但这对我的情况没有帮助。
控制器:
[Authorize]
[HttpGet]
public async Task<ActionResult> GetUserDetails()
{
try
{
int userId = User.Identity.GetUserId<int>();
var result = await _functionsService.GetUserDetails(userId);
if (result == null)
{
return Json(new { success = false, message = "User not found" }, JsonRequestBehavior.AllowGet);
}
return Json(new { success = true, data = result }, JsonRequestBehavior.AllowGet);
}
catch (Exception ex)
{
// Log the exception (ex) here as needed
return Json(new { success = false, message = "An error occurred while processing your request." }, JsonRequestBehavior.AllowGet);
}
}
服务:
public async Task<UserDetails> GetUserDetails(int userId)
{
var logEntry = new UserRequestLog
{
UserId = userId.ToString(),
RequestType = "GetUserDetails",
RequestData = $"{{ \"userId\": {userId} }}",
Timestamp = DateTime.UtcNow,
IpAddress = GetIpAddress(),
UserAgent = GetUserAgent()
};
try
{
var user = await _context.AspNetUsers
.Where(u => u.Id == userId)
.Select(u => new UserDetails
{
FirstName = u.Firstname,
LastName = u.Lastname,
PhoneNumber = u.PhoneNumber,
Email = u.Email,
Address = u.Address,
Country = u.Country,
OwnershipType = u.DropdownOwnershipType != null ? u.DropdownOwnershipType.Type : null,
AccountType = u.DropdownAccountType != null ? u.DropdownAccountType.Type : null,
DateOfBirth = u.DateOfBirth ?? default(DateTime), // Handle nullable DateTime
basicStatusId = u.BasicStatusId
})
.FirstOrDefaultAsync();
logEntry.ResponseStatus = "Success";
logEntry.ResponseData = $"{{ \"user\": {JsonConvert.SerializeObject(user)} }}";
return user;
}
catch (Exception ex)
{
logEntry.ResponseStatus = "Error";
logEntry.ResponseData = $"{{ \"error\": \"{ex.Message}\" }}";
// Optionally, rethrow or handle the exception
throw;
}
finally
{
_context.UserRequestLogs.Add(logEntry);
await _context.SaveChangesAsync();
}
}
这看起来与并发冲突有关,要解决此问题,您可以向实体添加并发令牌(例如 RowVersion 列)并处理并发。
通常,我们可以使用本机数据库生成的并发令牌或应用程序管理的并发令牌来处理这种情况。
来自文档:
无论您的并发令牌如何设置,都要实现 乐观并发,您的应用程序必须正确处理这种情况 发生并发冲突和 DbUpdateConcurrencyException 的地方 被抛出;这称为解决并发冲突。
一种选择是简单地通知用户由于以下原因更新失败 冲突的变化;然后用户可以加载新数据并尝试 再次。或者,如果您的应用程序正在执行自动更新,它 可以简单地循环并在重新查询数据后立即重试。
解决并发冲突的更复杂的方法是合并 数据库中新值的待定更改。精确的 合并哪些值的详细信息取决于应用程序和 过程可以由用户界面指导,其中两组值 显示。
更多详情,可以参考这篇文章。