7

I have a method to update user claims within our app.

I am logged as an admin user who can edit other users.

I am trying to remove existing claims of one user and assign new ones.

When removing claims using UserManger I am getting ConcurrencyFailure as a result. (Sometimes it works but most of the time it returns failure error.)

Code: "ConcurrencyFailure"

Description: "Optimistic concurrency failure, object has been modified."

Method:

    public async Task<bool> AssignClaimsToUser(string id, List<string> newClaims)
    {
        bool success = false;

        ApplicationUser user = await _userManager.FindByIdAsync(id);
        List<Claim> userClaims = new List<Claim>();

        // Remove existing claims
        IList<Claim> existingClaims = await _userManager.GetClaimsAsync(user);
        var removal = await _userManager.RemoveClaimsAsync(user, existingClaims); // This fail

        if (removal.Succeeded)
        {
            success = true;

            // Add new claims
            foreach (string policy in newClaims)
            {
                userClaims.Add(new Claim(policy, string.Empty, ClaimValueTypes.String));
            }
            await _userManager.AddClaimsAsync(user, userClaims);
        }

        return success;
    }

Why is it happening and what to do in order to fix this problem?

I have this issue only when removing claims. I don't have it when calling other methods on IdentityUser.

EDIT

Debug output:

enter image description here

Microsoft.AspNetCore.Hosting.Internal.WebHost:Information: Request starting HTTP/1.1 POST http://localhost:47691/Users/ManageUsers_Update application/x-www-form-urlencoded; charset=UTF-8 246 Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware:Information: HttpContext.User merged via AutomaticAuthentication from authenticationScheme: Identity.Application. Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization was successful for user: user@mydomain.com. Microsoft.AspNetCore.Authorization.DefaultAuthorizationService:Information: Authorization was successful for user: user@mydomain.com. Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker:Information: Executing action method MyProject.Controllers.UsersController.ManageUsers_Update (MyProject) with arguments (Kendo.Mvc.UI.DataSourceRequest, MyProject.Views.ViewModels.ManageUsersViewModel) - ModelState is Valid Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__id_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [u].[Id], [u].[AccessFailedCount], [u].[ConcurrencyStamp], [u].[CustRef], [u].[Email], [u].[EmailConfirmed], [u].[IsEnabled], [u].[LockoutEnabled], [u].[LockoutEnd], [u].[NormalizedEmail], [u].[NormalizedUserName], [u].[PasswordHash], [u].[PhoneNumber], [u].[PhoneNumberConfirmed], [u].[SecurityStamp], [u].[TwoFactorEnabled], [u].[UserName] FROM [AspNetUsers] AS [u] WHERE [u].[Id] = @__id_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__custRef_0='?' (Size = 10)], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [c].[CustomerDetailsId], [c].[CustRef], [c].[CustomerDBConnectionString], [c].[Enabled], [c].[Name], [c].[UserLicenses] FROM [CustomerDetails] AS [c] WHERE [c].[CustRef] = @__custRef_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__custRef_0='?' (Size = 10)], CommandType='Text', CommandTimeout='30'] SELECT [x].[ID], [x].[AspNetUserId], [x].[CustRef], [x].[CustomerId], [x].[Email], [x].[FirstName], [x].[IsEnabled], [x].[IsMaster], [x].[ShowCosts], [x].[Surname] FROM [Users] AS [x] WHERE [x].[CustRef] = @__custRef_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__users_Id_0='?'], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [a].[ID], [a].[AspNetUserId], [a].[CustRef], [a].[CustomerId], [a].[Email], [a].[FirstName], [a].[IsEnabled], [a].[IsMaster], [a].[ShowCosts], [a].[Surname] FROM [Users] AS [a] WHERE [a].[ID] = @__users_Id_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__id_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [u].[Id], [u].[AccessFailedCount], [u].[ConcurrencyStamp], [u].[CustRef], [u].[Email], [u].[EmailConfirmed], [u].[IsEnabled], [u].[LockoutEnabled], [u].[LockoutEnd], [u].[NormalizedEmail], [u].[NormalizedUserName], [u].[PasswordHash], [u].[PhoneNumber], [u].[PhoneNumberConfirmed], [u].[SecurityStamp], [u].[TwoFactorEnabled], [u].[UserName] FROM [AspNetUsers] AS [u] WHERE [u].[Id] = @__id_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__normalizedUserName_0='?' (Size = 256)], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [u].[Id], [u].[AccessFailedCount], [u].[ConcurrencyStamp], [u].[CustRef], [u].[Email], [u].[EmailConfirmed], [u].[IsEnabled], [u].[LockoutEnabled], [u].[LockoutEnd], [u].[NormalizedEmail], [u].[NormalizedUserName], [u].[PasswordHash], [u].[PhoneNumber], [u].[PhoneNumberConfirmed], [u].[SecurityStamp], [u].[TwoFactorEnabled], [u].[UserName] FROM [AspNetUsers] AS [u] WHERE [u].[NormalizedUserName] = @__normalizedUserName_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (6ms) [Parameters=[@p16='?' (Size = 450), @p0='?', @p1='?' (Size = 4000), @p17='?' (Size = 4000), @p2='?' (Size = 4000), @p3='?' (Size = 256), @p4='?', @p5='?', @p6='?', @p7='?', @p8='?' (Size = 256), @p9='?' (Size = 256), @p10='?' (Size = 4000), @p11='?' (Size = 4000), @p12='?', @p13='?' (Size = 4000), @p14='?', @p15='?' (Size = 256)], CommandType='Text', CommandTimeout='30'] SET NOCOUNT ON; UPDATE [AspNetUsers] SET [AccessFailedCount] = @p0, [ConcurrencyStamp] = @p1, [CustRef] = @p2, [Email] = @p3, [EmailConfirmed] = @p4, [IsEnabled] = @p5, [LockoutEnabled] = @p6, [LockoutEnd] = @p7, [NormalizedEmail] = @p8, [NormalizedUserName] = @p9, [PasswordHash] = @p10, [PhoneNumber] = @p11, [PhoneNumberConfirmed] = @p12, [SecurityStamp] = @p13, [TwoFactorEnabled] = @p14, [UserName] = @p15 WHERE [Id] = @p16 AND [ConcurrencyStamp] = @p17; SELECT @@ROWCOUNT; Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__menuId_0='?'], CommandType='Text', CommandTimeout='30'] SELECT [u].[CustomerMenuId], [u].[MenuItemId] FROM [CustomerMenuItem] AS [u] WHERE [u].[CustomerMenuId] = @__menuId_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT [m].[Id], [m].[AspNetPolicyId], [m].[GlyphIcon], [m].[Label], [m].[MenuGroupId] FROM [MenuItem] AS [m] Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[], CommandType='Text', CommandTimeout='30'] SELECT [a].[Id], [a].[DefaultAction], [a].[Description], [a].[Name] FROM [AspNetPolicy] AS [a] Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__id_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [u].[Id], [u].[AccessFailedCount], [u].[ConcurrencyStamp], [u].[CustRef], [u].[Email], [u].[EmailConfirmed], [u].[IsEnabled], [u].[LockoutEnabled], [u].[LockoutEnd], [u].[NormalizedEmail], [u].[NormalizedUserName], [u].[PasswordHash], [u].[PhoneNumber], [u].[PhoneNumberConfirmed], [u].[SecurityStamp], [u].[TwoFactorEnabled], [u].[UserName] FROM [AspNetUsers] AS [u] WHERE [u].[Id] = @__id_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__user_Id_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30'] SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId] FROM [AspNetUserClaims] AS [uc] WHERE [uc].[UserId] = @__user_Id_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (9ms) [Parameters=[@__8__locals1_user_Id_0='?' (Size = 450), @__claim_Value_1='?' (Size = 4000), @__claim_Type_2='?' (Size = 4000)], CommandType='Text', CommandTimeout='30'] SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId] FROM [AspNetUserClaims] AS [uc] WHERE (([uc].[UserId] = @__8__locals1_user_Id_0) AND ([uc].[ClaimValue] = @__claim_Value_1)) AND ([uc].[ClaimType] = @__claim_Type_2) Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (6ms) [Parameters=[@__8__locals1_user_Id_0='?' (Size = 450), @__claim_Value_1='?' (Size = 4000), @__claim_Type_2='?' (Size = 4000)], CommandType='Text', CommandTimeout='30'] SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId] FROM [AspNetUserClaims] AS [uc] WHERE (([uc].[UserId] = @__8__locals1_user_Id_0) AND ([uc].[ClaimValue] = @__claim_Value_1)) AND ([uc].[ClaimType] = @__claim_Type_2) Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (93ms) [Parameters=[@__8__locals1_user_Id_0='?' (Size = 450), @__claim_Value_1='?' (Size = 4000), @__claim_Type_2='?' (Size = 4000)], CommandType='Text', CommandTimeout='30'] SELECT [uc].[Id], [uc].[ClaimType], [uc].[ClaimValue], [uc].[UserId] FROM [AspNetUserClaims] AS [uc] WHERE (([uc].[UserId] = @__8__locals1_user_Id_0) AND ([uc].[ClaimValue] = @__claim_Value_1)) AND ([uc].[ClaimType] = @__claim_Type_2) Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (5ms) [Parameters=[@__normalizedUserName_0='?' (Size = 256)], CommandType='Text', CommandTimeout='30'] SELECT TOP(1) [u].[Id], [u].[AccessFailedCount], [u].[ConcurrencyStamp], [u].[CustRef], [u].[Email], [u].[EmailConfirmed], [u].[IsEnabled], [u].[LockoutEnabled], [u].[LockoutEnd], [u].[NormalizedEmail], [u].[NormalizedUserName], [u].[PasswordHash], [u].[PhoneNumber], [u].[PhoneNumberConfirmed], [u].[SecurityStamp], [u].[TwoFactorEnabled], [u].[UserName] FROM [AspNetUsers] AS [u] WHERE [u].[NormalizedUserName] = @__normalizedUserName_0 Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (6ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p19='?' (Size = 450), @p3='?', @p4='?' (Size = 4000), @p20='?' (Size = 4000), @p5='?' (Size = 4000), @p6='?' (Size = 256), @p7='?', @p8='?', @p9='?', @p10='?', @p11='?' (Size = 256), @p12='?' (Size = 256), @p13='?' (Size = 4000), @p14='?' (Size = 4000), @p15='?', @p16='?' (Size = 4000), @p17='?', @p18='?' (Size = 256)], CommandType='Text', CommandTimeout='30'] SET NOCOUNT ON; DELETE FROM [AspNetUserClaims] WHERE [Id] = @p0; SELECT @@ROWCOUNT; DELETE FROM [AspNetUserClaims] WHERE [Id] = @p1; SELECT @@ROWCOUNT; DELETE FROM [AspNetUserClaims] WHERE [Id] = @p2; SELECT @@ROWCOUNT; UPDATE [AspNetUsers] SET [AccessFailedCount] = @p3, [ConcurrencyStamp] = @p4, [CustRef] = @p5, [Email] = @p6, [EmailConfirmed] = @p7, [IsEnabled] = @p8, [LockoutEnabled] = @p9, [LockoutEnd] = @p10, [NormalizedEmail] = @p11, [NormalizedUserName] = @p12, [PasswordHash] = @p13, [PhoneNumber] = @p14, [PhoneNumberConfirmed] = @p15, [SecurityStamp] = @p16, [TwoFactorEnabled] = @p17, [UserName] = @p18 WHERE [Id] = @p19 AND [ConcurrencyStamp] = @p20; SELECT @@ROWCOUNT; 'dotnet.exe' (CoreCLR: clrhost): Loaded 'C:\Users\jsmith.nuget\packages\system.diagnostics.stacktrace\4.3.0\lib\netstandard1.3\System.Diagnostics.StackTrace.dll'. Skipped loading symbols. Module is optimized and the debugger option 'Just My Code' is enabled. Microsoft.EntityFrameworkCore.DbContext:Error: An exception occurred in the database while saving changes. 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.d__6.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.d__32.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.d__1.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.d__47.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.d__45.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.DbContext.d__30.MoveNext()

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.d__6.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.d__2.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.d__32.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.d__1.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.d__47.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.d__45.MoveNext() --- End of stack trace from previous location where exception was thrown --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.EntityFrameworkCore.DbContext.d__30.MoveNext() Microsoft.EntityFrameworkCore.Storage.Internal.RelationalCommandBuilderFactory:Information: Executed DbCommand (6ms) [Parameters=[@p0='?', @p1='?', @p2='?', @p19='?' (Size = 450), @p3='?', @p4='?' (Size = 4000), @p20='?' (Size = 4000), @p5='?' (Size = 4000), @p6='?' (Size = 256), @p7='?', @p8='?', @p9='?', @p10='?', @p11='?' (Size = 256), @p12='?' (Size = 256), @p13='?' (Size = 4000), @p14='?' (Size = 4000), @p15='?', @p16='?' (Size = 4000), @p17='?', @p18='?' (Size = 256)], CommandType='Text', CommandTimeout='30'] SET NOCOUNT ON; DELETE FROM [AspNetUserClaims] WHERE [Id] = @p0; SELECT @@ROWCOUNT; DELETE FROM [AspNetUserClaims] WHERE [Id] = @p1; SELECT @@ROWCOUNT; DELETE FROM [AspNetUserClaims] WHERE [Id] = @p2; SELECT @@ROWCOUNT; UPDATE [AspNetUsers] SET [AccessFailedCount] = @p3, [ConcurrencyStamp] = @p4, [CustRef] = @p5, [Email] = @p6, [EmailConfirmed] = @p7, [IsEnabled] = @p8, [LockoutEnabled] = @p9, [LockoutEnd] = @p10, [NormalizedEmail] = @p11, [NormalizedUserName] = @p12, [PasswordHash] = @p13, [PhoneNumber] = @p14, [PhoneNumberConfirmed] = @p15, [SecurityStamp] = @p16, [TwoFactorEnabled] = @p17, [UserName] = @p18 WHERE [Id] = @p19 AND [ConcurrencyStamp] = @p20; SELECT @@ROWCOUNT; Microsoft.EntityFrameworkCore.DbContext:Error: An exception occurred in the database while saving changes. 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.ConsumeResultSetWithoutPropagation(Int32 commandIndex, DbDataReader reader) at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.Consume(DbDataReader reader) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable1 commandBatches, IRelationalConnection connection) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IReadOnlyList1 entriesToSave) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess) at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess)

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.ConsumeResultSetWithoutPropagation(Int32 commandIndex, DbDataReader reader) at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.Consume(DbDataReader reader) at Microsoft.EntityFrameworkCore.Update.ReaderModificationCommandBatch.Execute(IRelationalConnection connection) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.Execute(IEnumerable1 commandBatches, IRelationalConnection connection) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(IReadOnlyList1 entriesToSave) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChanges(Boolean acceptAllChangesOnSuccess) at Microsoft.EntityFrameworkCore.DbContext.SaveChanges(Boolean acceptAllChangesOnSuccess) Exception thrown: 'Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException' in Microsoft.EntityFrameworkCore.dll The thread 0x6264 has exited with code 0 (0x0). The thread 0xaa0 has exited with code 0 (0x0). The thread 0x6a84 has exited with code 0 (0x0).

ManageUsers_Update:

    public async  Task<ActionResult> ManageUsers_Update([DataSourceRequest] DataSourceRequest request, ManageUsersViewModel viewModel)
    {
        if (viewModel != null && ModelState.IsValid)
        {
            User objToUpdate = new User
            {
                Id = viewModel.ID,
                FirstName = viewModel.FirstName,
                Surname = viewModel.Surname
            };
            _userRepository.UpdateUser(objToUpdate);

            if (viewModel.CustomerMenuId != null && viewModel.CustomerMenuId > 0)
            {
                int customerMenuId = Convert.ToInt32(viewModel.CustomerMenuId);

                // Update user's claims
                List<string> newClaims = _navigationRepository.GetPolicyNamesByCustomerMenuId(customerMenuId);
                bool claimsUpdated = await _applicationUserService.AssignClaimsToUser(viewModel.AspNetUserId, newClaims);

                // Assign user to menu if updating claims succeeded
                if (claimsUpdated)
                {
                    _navigationRepository.AssignCustomerMenuToUser(new CustomerMenuUser()
                        {CustomerMenuId = customerMenuId, AspNetUserId = viewModel.AspNetUserId});
                }
            }

            ApplicationUser user = await _userManager.FindByIdAsync(viewModel.AspNetUserId);
            user.IsEnabled = viewModel.IsEnabled;
            await _userManager.UpdateAsync(user);
        }

        return Json(new[] { viewModel }.ToDataSourceResult(request, ModelState));
    }
nickornotto
  • 1,946
  • 4
  • 36
  • 68

4 Answers4

3

In my case, I get the same error while using _roleManager.UpdateAsync I was sending a role model to update method and then that gave me the error and I thought that I was getting this error because of the process of retrieving data that I was running to load the data in the screen (FindByIdAsync to get the claims and others)

Performing those get from the role manager Maybe keeps the entity in memory since we are using Entity framework, so what I did was just to use _roleManager.FindByIdAsync before the update command and that will give me the current entity that I should update.

Okyam
  • 700
  • 6
  • 19
2

Why is it happening?

The most probable reason because of a concurrency conflict, which means multiple users (threads) have made an attempt to change the same data.

Here you may find more details on how it occurs

https://learn.microsoft.com/en-us/ef/core/saving/concurrency

What to do in order to fix this problem?

I assume that in your case multiple users are not supposed to update the same Claims, thus there might exist request duplication.

You may try:

  • check if the data is updated in the DB. In case of multiple requests, one of them should save the changes;
  • check Network tab in your browser while posting the request or put a break point and look how much times it was hit during a single request;
  • isolate the defective code from the application and try to reproduce that issue.

Another approach (usually when multiple access is allowed) is to implement retry policy to re-execute update logic in case of optimistic failure. I'm pretty sure this is not suitable for your case, but this will work if you need a quick solution.

Ivan Zaruba
  • 4,236
  • 5
  • 20
  • 29
  • 1
    It's not possible for multiple users to attempt to update the same record as I am the only user using/ working on it. Usually first change is successful, the following attempts fail even after I log out and login again. 1. The successful removal updates the claims correctly as it's supposed to do. 2. Te request is made only once. @Ivan – nickornotto Sep 28 '18 at 14:39
  • @nickornotto would be great if you shared a repo with the code to reproduce the issue – Ivan Zaruba Sep 28 '18 at 14:58
  • I can share more information if needed but can't share the project. It also involves the database and is large – nickornotto Sep 28 '18 at 15:07
  • Could you provide the debug output? – Ivan Zaruba Sep 28 '18 at 15:08
  • added the debug output to the post – nickornotto Oct 01 '18 at 08:34
  • That is not exactly what I expected. By Debug output I mean the text from Output window (usually opened with Ctrl+Alt+O) and selected Show output from: Debug. This shows how the response is processed and what queries are made to the DB. – Ivan Zaruba Oct 01 '18 at 10:22
  • Added the output @Ivan – nickornotto Oct 01 '18 at 11:50
  • Is it possible to see ManageUsers_Update action to see what happens before AssignClaimsToUser is called? – Ivan Zaruba Oct 01 '18 at 13:39
  • Not much is happening, nothing really related to `ApplicationUser` @Ivan, see above – nickornotto Oct 01 '18 at 14:26
  • 1
    I tried to use the catch from MS article you referenced but it can't fix the main issue: removing the existing claims which fails. The catch snippet handle the properties of the `EntityEntry` only which is `ApplicationUser` which is correct and in no need of updating. Just the concurrency stamp is changing. – nickornotto Oct 01 '18 at 14:33
  • Unable to reproduce the issue. The only way to catch that issue for me is when I post two requests at a same time. In that case the reason is obvious in a Debug output. It doesn't seem so in your case. Not sure if I could help unless you provided some code to reproduce that. Maybe you could rename stuff throughout and isolate that only action. Anyway, if not, you could try to update versions of EF Core packages. Also try to mock previous calls that use `DbContext` - comment out `userRepositoty.UpdateUser()` and replace `newClaims` with a constant value to ensure those invocations do not affect – Ivan Zaruba Oct 01 '18 at 15:19
  • `newClaims` is just a list of strings - and it is populated from another table not related to `AspNetUsers` - no reason it could affect `UserManager`. `UpdateUser` also uses another custom users table. I hardcoded the values instead of pulling from the table, removed all code not necessary to update the claims, and it had no positive effect - the issue still persist. I think I'll write a custom sproc first to remove claims from `AspNetUserClaims` and see whether that fix the issue. – nickornotto Oct 04 '18 at 08:41
  • I managed to update the concurrency stamp and re-run removing claims in the catch loop and it works every time now – nickornotto Oct 04 '18 at 09:39
  • @nickornotto great to hear that – Ivan Zaruba Oct 04 '18 at 11:50
  • Any reference for the retry and re-run approach to handling optimistic concurrency in the `catch` block? – Kok How Teh Oct 17 '19 at 13:54
  • @Kok How Teh it's just a try/catch with the same method call in catch clause. However, be aware that a proper way would be to synchronize access either at code level or at DB level (which allows more detailed locks) or change concurrency mode to pesimistic. Re-trying an optimistic failure is a workaround rather than the best practice. – Ivan Zaruba Oct 17 '19 at 14:16
0

In my case solution was simple: I had Products, Categories and ProductCategories when I removed category before products (which also remove data from ProductCategories) I got an error just like you. The problem was he was trying remove relation which doesn't have opportunity to exist because categories already been removed.

Kenik
  • 103
  • 1
  • 15
0

I'm not sure if this helps because it is a little bit unrelated, but I had the same concurrency error with code "ConcurrencyFailure" (and with description "Optimistic concurrency failure, object has been modified.") with the following steps and might help someone finding this question trying to solve the same issue as me:

  • I created a .NET Core 5 API with it's own unit test project

  • I created my own Domain.IdentityUser (derived from IdentityUser) and Domain.IdentityRole (derived from IdentityRole which had to set NormalizedName in order for the role to be found by UserManager<Domain.IdentityUser>)

  • I created a unit test creating a new instance of UserManager<Domain.IdentityUser> (with the store constructor argument set as:

    new UserStore<Domain.IdentityUser, Domain.IdentityRole, ApplicationDbContext, long>(applicationDbContext)
    

    and the logger constructor argument of the UserManager<Domain.IdentityUser> constructor set as:

    new Microsoft.Extensions.Logging.Logger<UserManager<Domain.IdentityUser>>(new Microsoft.Extensions.Logging.LoggerFactory())
    

    with all other arguments of UserManager<Domain.IdentityUser> constructor set as null).

  • The store UserStore<Domain.IdentityUser, Domain.IdentityRole, ApplicationDbContext, long> didn't receive the same ApplicationDbContext as constructor argument as the rest of the unit test, so every time the unit test called "userManager.AddToRolesAsync(loadedUser, roles);", I received the same concurrency error as printed by some of my unit test code regarding the AddToRolesAsync returned IdentityResult serialized as json:

     {
       "Succeeded": false,
       "Errors": [
         {
           "Code": "ConcurrencyFailure",
           "Description": "Optimistic concurrency failure, object has been modified."
         }
       ]
     }
    

I had to use the same ApplicationDbContext instance everywhere in the unit test, along with loading data from the in-memory database instead of relying on newly created objects.