C# Identity Usermanager DeleteAsync DbUpdateConcurrencyException
我试图通过webapi后面的aspnetcore.identity UserManager删除用户C# Identity Usermanager DeleteAsync DbUpdateConcurrencyException,c#,asp.net-core,entity-framework-core,asp.net-core-identity,C#,Asp.net Core,Entity Framework Core,Asp.net Core Identity,我试图通过webapi后面的aspnetcore.identity UserManager删除用户 [HttpPost("Delete", Name = "DeleteRoute")] [Authorize(Roles = "SuperUser")] public async Task<IActionResult> DeleteAsync([FromBody] User user) { Console.WriteLine("Deleti
[HttpPost("Delete", Name = "DeleteRoute")]
[Authorize(Roles = "SuperUser")]
public async Task<IActionResult> DeleteAsync([FromBody] User user)
{
Console.WriteLine("Deleting user: " + user.Id);
try {
await _userManager.DeleteAsync(user);
return Ok();
} catch(Exception e) {
return BadRequest(e.Message);
}
}
我知道这个异常通常表示比赛条件,但我不明白为什么会发生这种情况
我做错什么了吗
编辑
我发布的用户对象如下所示:
"User": {
"Email": "",
"FirstName": "",
"LastName": "",
"Gender": "",
"Affiliation": {
"isStudent": true,
"isEmployee": false
}
...
}
实体框架核心使用: 在乐观并发模型中,如果一个用户从数据库接收到一个值后,另一个用户在第一个用户试图修改该值之前修改了该值,则认为发生了冲突 与悲观并发性相比: …在悲观并发模型中,更新行的用户建立锁。在用户完成更新并释放锁之前,其他人无法更改该行 为了实现乐观并发性,
IdentityUser
类包含一个属性(以及数据库中相应的列),它是GUID的字符串表示形式:
public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();
每次将用户保存到数据库时,ConcurrencyStamp
都会设置为新的GUID
以删除用户为例,发送到服务器的DELETE
SQL语句的简化版本可能如下所示:
从dbo.AspNetUsers中删除
其中Id=''和ConcurrencyStamp=''
当上面SQL语句中的并发\u STAMP
值与数据库中存储的给定用户的值不匹配时,会出现您收到的错误消息。这确保了如果从数据库(其中包含特定的ConcurrencyStamp
)检索用户,则只有在其他地方未进行任何更改时(因为您提供的是数据库中存在的相同ConcurrencyStamp
值),才能保存对数据库的更改
从上面的ConcurrencyStamp
定义中可以看到,该属性默认为一个新的GUID
——每次创建IdentityUser
(或子类)时,它都会获得一个新的ConcurrencyStamp
值。在您的示例中,使用传递到DeleteAsync
操作的User
,ASP.NET核心模型绑定首先创建User
的新实例,然后设置JSON负载中存在的属性。由于有效负载中没有ConcurrencyStamp
值,User
将以新的ConcurrencyStamp
值结束,该值与数据库中的值不匹配
为了避免此问题,您可以将ConcurrencyStamp
值添加到从客户端发送的有效负载中。但是,我不建议这样做。解决此问题的最简单和最安全的方法是将用户的Id
作为有效负载发送,使用\u userManager.FindByIdAsync
检索用户本身,然后使用该实例执行删除。下面是一个例子:
[HttpPost("Delete/{id}", Name = "DeleteRoute")]
[Authorize(Roles = "SuperUser")]
public async Task<IActionResult> DeleteAsync(string id)
{
Console.WriteLine("Deleting user: " + id);
try {
var user = await _userManager.FindByIdAsync(id);
if(user == null)
// ...
await _userManager.DeleteAsync(user);
return Ok();
} catch(Exception e) {
return BadRequest(e.Message);
}
}
[HttpPost(“Delete/{id}”,Name=“DeleteRoute”)]
[授权(角色=“超级用户”)]
公共异步任务DeleteAsync(字符串id)
{
Console.WriteLine(“删除用户:+id”);
试一试{
var user=await\u userManager.FindByIdAsync(id);
if(user==null)
// ...
wait_userManager.deleteAync(用户);
返回Ok();
}捕获(例外e){
返回请求(e.Message);
}
}
在保存过程中,如果意外的行数受到影响,则会发生并发冲突。这通常是因为数据库中的数据在加载到内存后已被修改。()在您的情况下,控制器接收的用户对象在数据库中可能不具有相同的值。正如@KirkLarkin所建议的,您可以使用用户ID获取用户对象,然后调用delete方法。
[HttpPost("Delete/{id}", Name = "DeleteRoute")]
[Authorize(Roles = "SuperUser")]
public async Task<IActionResult> DeleteAsync(string id)
{
Console.WriteLine("Deleting user: " + id);
try {
var user = await _userManager.FindByIdAsync(id);
if(user == null)
// ...
await _userManager.DeleteAsync(user);
return Ok();
} catch(Exception e) {
return BadRequest(e.Message);
}
}