簡體   English   中英

身份用戶管理器DeleteAsync DbUpdateConcurrencyException

[英]Identity Usermanager DeleteAsync DbUpdateConcurrencyException

我試圖通過webapi后面的aspnetcore.identity UserManager刪除用戶。

    [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);
        }

    }

這將引發DbUpdateConcurrencyException

   Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyException(Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected)
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithoutPropagationAsync(Int32 commandIndex, RelationalDataReader reader, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeAsync(RelationalDataReader reader, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(DbContext _, ValueTuple`2 parameters, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IReadOnlyList`1 entriesToSave, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: Database operation expected to affect 1 row(s) but actually affected 0 row(s). Data may have been modified or deleted since entities were loaded. See http://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyException(Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected)
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithoutPropagationAsync(Int32 commandIndex, RelationalDataReader reader, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeAsync(RelationalDataReader reader, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(DbContext _, ValueTuple`2 parameters, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func`4 operation, Func`4 verifySucceeded, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IReadOnlyList`1 entriesToSave, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
   at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)

我知道這個例外通常表示比賽條件,但我不知道為什么會這樣。

難道我做錯了什么?

編輯

我發布的用戶對象看起來像這樣:

"User": {
 "Email": "",
 "FirstName": "",
 "LastName": "",
 "Gender": "",
 "Affiliation": {
     "isStudent": true,
     "isEmployee": false
   }
   ...
}

實體框架核心使用樂觀並發

在樂觀並發模型中,如果在用戶從數據庫中接收到值之后,另一個用戶在第一個用戶嘗試修改值之前修改了該值,則認為發生了違規。

將此與悲觀並發進行對比:

在悲觀的並發模型中,更新行的用戶將建立鎖。 在用戶完成更新並釋放鎖之前,沒有其他人可以更改該行。

為了實現樂觀並發, IdentityUser類包含一個ConcurrencyStamp屬性(以及數據庫中的相應列),它是GUID的字符串表示形式:

public virtual string ConcurrencyStamp { get; set; } = Guid.NewGuid().ToString();

每次將用戶保存到數據庫時, ConcurrencyStamp都會設置為新的GUID。

以刪除用戶為例,發送到服務器的DELETE SQL語句的簡化版本可能類似於以下內容:

DELETE FROM dbo.AspNetUsers WHERE Id = '<USER_ID>' AND ConcurrencyStamp = '<CONCURRENCY_STAMP>'

當上面的SQL語句中的CONCURRENCY_STAMP值與給定用戶的數據庫中存儲的值不匹配時,會出現您收到的錯誤消息。 這樣可以確保,如果您從數據庫(包含特定的ConcurrencyStamp )中檢索用戶,則只有在其他地方沒有其他更改的情況下,才能將更改保存到數據庫中(因為您要提供數據庫中存在的相同ConcurrencyStamp值) )。

從上面的ConcurrencyStamp定義可以看到,該類屬性默認為新的GUID每次創建IdentityUser (或子類)時,都會為其分配新的ConcurrencyStamp值。 在您的示例中,通過將User傳遞給DeleteAsync操作,ASP.NET Core Model-Binding首先創建一個User的新實例,然后設置JSON有效負載中存在的屬性。 由於有效負載中沒有ConcurrencyStamp值,因此User最終將獲得一個新的 ConcurrencyStamp值,該值當然與數據庫中的值不匹配。

為避免此問題,您可以ConcurrencyStamp值添加到客戶端發送的有效負載中。 但是,我不建議這樣做。 解決此問題的最簡單,最安全的方法是,簡單地發送UserId作為有效載荷,使用_userManager.FindByIdAsync檢索User本身,然后使用實例執行刪除。 這是一個例子:

[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);
    }
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM