Hey,
We are running ABP 9.3.2 with Angular and are experiencing issues where it logs out users on the frontend, directs to backend and relogs in. During this occurring I have seen this in the console.
[09:34:10 DBG] The event OpenIddict.Server.OpenIddictServerEvents+GenerateTokenContext was successfully processed by OpenIddict.Server.OpenIddictServerHandlers+Protection+CreateTokenEntry.
[09:34:10 DBG] An exception was thrown by OpenIddict.Server.OpenIddictServerHandlers+Protection+CreateTokenEntry while handling the OpenIddict.Server.OpenIddictServerEvents+GenerateTokenContext event.
Volo.Abp.Data.AbpDbConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See https://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
---> Microsoft.EntityFrameworkCore.DbUpdateConcurrencyException: The database operation was expected to affect 1 row(s), but actually affected 0 row(s); data may have been modified or deleted since entities were loaded. See https://go.microsoft.com/fwlink/?LinkId=527962 for information on understanding and handling optimistic concurrency exceptions.
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ThrowAggregateUpdateConcurrencyExceptionAsync(RelationalDataReader reader, Int32 commandIndex, Int32 expectedRowsAffected, Int32 rowsAffected, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.AffectedCountModificationCommandBatch.ConsumeResultSetWithRowsAffectedOnlyAsync(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.ReaderModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.SqlServer.Update.Internal.SqlServerModificationCommandBatch.ExecuteAsync(IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.Update.Internal.BatchExecutor.ExecuteAsync(IEnumerable1 commandBatches, IRelationalConnection connection, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.Storage.RelationalDatabase.SaveChangesAsync(IList1 entries, CancellationToken cancellationToken)
at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(IList1 entriesToSave, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.ChangeTracking.Internal.StateManager.SaveChangesAsync(StateManager stateManager, Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.SqlServer.Storage.Internal.SqlServerExecutionStrategy.ExecuteAsync[TState,TResult](TState state, Func4 operation, Func4 verifySucceeded, CancellationToken cancellationToken) at Microsoft.EntityFrameworkCore.DbContext.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Volo.Abp.EntityFrameworkCore.AbpDbContext1.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken)
--- End of inner exception stack trace ---
at Volo.Abp.EntityFrameworkCore.AbpDbContext1.SaveChangesAsync(Boolean acceptAllChangesOnSuccess, CancellationToken cancellationToken) at Volo.Abp.Domain.Repositories.EntityFrameworkCore.EfCoreRepository2.InsertAsync(TEntity entity, Boolean autoSave, CancellationToken cancellationToken)
at Castle.DynamicProxy.AsyncInterceptorBase.ProceedAsynchronous[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo)
at Volo.Abp.Castle.DynamicProxy.CastleAbpMethodInvocationAdapterWithReturnValue1.ProceedAsync() at Volo.Abp.Uow.UnitOfWorkInterceptor.InterceptAsync(IAbpMethodInvocation invocation) at Volo.Abp.Castle.DynamicProxy.CastleAsyncAbpInterceptorAdapter1.InterceptAsync[TResult](IInvocation invocation, IInvocationProceedInfo proceedInfo, Func3 proceed) at Volo.Abp.OpenIddict.Tokens.AbpOpenIddictTokenStore.CreateAsync(OpenIddictTokenModel token, CancellationToken cancellationToken) at OpenIddict.Core.OpenIddictTokenManager1.CreateAsync(TToken token, CancellationToken cancellationToken)
at OpenIddict.Core.OpenIddictTokenManager1.CreateAsync(OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken) at OpenIddict.Core.OpenIddictTokenManager1.OpenIddict.Abstractions.IOpenIddictTokenManager.CreateAsync(OpenIddictTokenDescriptor descriptor, CancellationToken cancellationToken)
at OpenIddict.Server.OpenIddictServerHandlers.Protection.CreateTokenEntry.HandleAsync(GenerateTokenContext context)
...
at OpenIddict.Server.AspNetCore.OpenIddictServerAspNetCoreHandler.SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) at Microsoft.AspNetCore.Authentication.AuthenticationService.SignInAsync(HttpContext context, String scheme, ClaimsPrincipal principal, AuthenticationProperties properties) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultAsync>g__Logged|22_0(ResourceInvoker invoker, IActionResult result) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResultFilterAsync>g__Awaited|30_0[TFilter,TFilterAsync](ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResultExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.ResultNext[TFilter,TFilterAsync](State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeResultFilters>g__Awaited|28_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeNextResourceFilter>g__Awaited|25_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Rethrow(ResourceExecutedContextSealed context) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeFilterPipelineAsync>g__Awaited|20_0(ResourceInvoker invoker, Task lastTask, State next, Scope scope, Object state, Boolean isCompleted) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Mvc.Infrastructure.ResourceInvoker.<InvokeAsync>g__Logged|17_1(ResourceInvoker invoker) at Microsoft.AspNetCore.Routing.EndpointMiddleware.<Invoke>g__AwaitRequestTask|7_0(Endpoint endpoint, Task requestTask, ILogger logger) at Volo.Abp.AspNetCore.Tracing.AbpCorrelationIdMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at DevExpress.AspNetCore.Internal.ResourceManagerMiddleware.Invoke(HttpContext httpContext) at Volo.Abp.AspNetCore.Serilog.AbpSerilogMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Volo.Abp.AspNetCore.Auditing.AbpAuditingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Volo.Abp.AspNetCore.Auditing.AbpAuditingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Authorization.AuthorizationMiddleware.Invoke(HttpContext context) at Volo.Abp.AspNetCore.Security.Claims.AbpDynamicClaimsMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Volo.Abp.AspNetCore.Uow.AbpUnitOfWorkMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Volo.Abp.AspNetCore.ExceptionHandling.AbpExceptionHandlingMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Volo.Abp.AspNetCore.MultiTenancy.MultiTenancyMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Builder.ApplicationBuilderAbpOpenIddictMiddlewareExtension.<>c__DisplayClass0_0.<<UseAbpOpenIddictValidation>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context) at Volo.Abp.AspNetCore.Security.AbpSecurityHeadersMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Microsoft.AspNetCore.Localization.RequestLocalizationMiddleware.Invoke(HttpContext context) at Microsoft.AspNetCore.RequestLocalization.AbpRequestLocalizationMiddleware.InvokeAsync(HttpContext context, RequestDelegate next) at Microsoft.AspNetCore.Builder.UseMiddlewareExtensions.InterfaceMiddlewareBinder.<>c__DisplayClass2_0.<<CreateMiddleware>b__0>d.MoveNext() --- End of stack trace from previous location --- at Swashbuckle.AspNetCore.SwaggerUI.SwaggerUIMiddleware.Invoke(HttpContext httpContext) at Swashbuckle.AspNetCore.Swagger.SwaggerMiddleware.Invoke(HttpContext httpContext, ISwaggerProvider swaggerProvider) at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context) [09:34:10 DBG] The event OpenIddict.Server.OpenIddictServerEvents+GenerateTokenContext was successfully processed by OpenIddict.Server.OpenIddictServerHandlers+Protection+GenerateIdentityModelToken. [09:34:10 DBG] The event OpenIddict.Server.OpenIddictServerEvents+ProcessSignInContext was successfully processed by OpenIddict.Server.OpenIddictServerHandlers+GenerateAccessToken. [09:34:10 DBG] The event OpenIddict.Server.OpenIddictServerEvents+GenerateTokenContext was successfully processed by OpenIddict.Server.OpenIddictServerHandlers+Protection+AttachSecurityCredentials. [09:34:10 INF] Executed action Volo.Abp.OpenIddict.Controllers.TokenController.HandleAsync (Volo.Abp.OpenIddict.AspNetCore) in 282.0944ms [09:34:10 INF] Executed endpoint 'Volo.Abp.OpenIddict.Controllers.TokenController.HandleAsync (Volo.Abp.OpenIddict.AspNetCore)' [09:34:10 WRN] There is an entry which is not saved due to concurrency exception: OpenIddictAuthorization {Id: (id)} Modified FK {ApplicationId: (appid)}
This doesn't seem to occur when I add a solution you provided for something simular from posts below: https://abp.io/support/questions/8917/VoloAbpDataAbpDbConcurrencyException-The-database-operation-was-expected-to-affect-1-rows https://abp.io/support/questions/8068/Suspicious-updates-on-OpenIddictApplication-when-token-endpoint-is-called-with-clientcredentials-granttype#answer-3a158dd3-918a-fd76-1e37-5cfe204100e5 https://abp.io/support/questions/9298/ABP-9-Upgrade---login-screen-409-error
I added: using Microsoft.EntityFrameworkCore.ChangeTracking; using Volo.Abp.DependencyInjection; using Volo.Abp.EntityFrameworkCore.ChangeTrackers; using Volo.Abp.OpenIddict.Tokens;
namespace ePlus.EntityFrameworkCore;
[Dependency(ReplaceServices = true)] [ExposeServices(typeof(AbpEfCoreNavigationHelper))] public class ePlusAbpEfCoreNavigationHelper : AbpEfCoreNavigationHelper { public override void ChangeTracker_Tracked(object? sender, EntityTrackedEventArgs e) { if (e.Entry.Entity.GetType() == typeof(OpenIddictToken)) { return; }
base.ChangeTracker_Tracked(sender, e);
}
public override void ChangeTracker_StateChanged(object? sender, EntityStateChangedEventArgs e)
{
if (e.Entry.Entity.GetType() == typeof(OpenIddictToken))
{
return;
}
base.ChangeTracker_StateChanged(sender, e);
}
}
I added this to our EntityFrameworkCore module and issue doesn't seem to persist, is that recommended or should I try something else? Also I believed this should have been fixed by my current version?
Kind regards, Josh.
2 Answer(s)
-
0
- Solution
- What you’re seeing: The log shows OpenIddict trying to persist a token/authorization entry and hitting EF Core optimistic concurrency (AbpDbConcurrencyException) during GenerateTokenContext → Protection.CreateTokenEntry. This typically happens when EF’s ChangeTracker tries to attach/update OpenIddict entities that were modified concurrently or were already being tracked differently during token issuance.
- Why your workaround helps: Overriding AbpEfCoreNavigationHelper and skipping ChangeTracker processing for OpenIddictToken prevents ABP’s navigation helper from re-attaching or fixing navigations for OpenIddictToken entities. That avoids EF Core performing unintended updates that can trigger concurrency exceptions.
- Is it recommended? Yes, this is an accepted workaround for cases where EF Core tracking interferes with OpenIddict token persistence. The same pattern has been used in prior support answers to stop the navigation helper from manipulating OpenIddict entities during token issuance. Your code is correct and safe:
using Microsoft.EntityFrameworkCore.ChangeTracking; using Volo.Abp.DependencyInjection; using Volo.Abp.EntityFrameworkCore.ChangeTrackers; using Volo.Abp.OpenIddict.Tokens; [Dependency(ReplaceServices = true)] [ExposeServices(typeof(AbpEfCoreNavigationHelper))] public class ePlusAbpEfCoreNavigationHelper : AbpEfCoreNavigationHelper { public override void ChangeTracker_Tracked(object? sender, EntityTrackedEventArgs e) { if (e.Entry.Entity.GetType() == typeof(OpenIddictToken)) { return; } base.ChangeTracker_Tracked(sender, e); } public override void ChangeTracker_StateChanged(object? sender, EntityStateChangedEventArgs e) { if (e.Entry.Entity.GetType() == typeof(OpenIddictToken)) { return; } base.ChangeTracker_StateChanged(sender, e); } }- Addresses the auto logout symptom: When token persistence throws, the server still generates a JWT (GenerateIdentityModelToken) but fails storing server-side token state in some cases. That can cause the Angular app to momentarily lose continuity and re-trigger login/refresh flows. Preventing the concurrency exception stabilizes token flows and avoids visible “logout/login” flickers.
- Should this be fixed in 9.3.2 already? ABP has addressed several OpenIddict-related tracking/concurrency issues across 8.x–9.x, but depending on usage and customizations, EF’s navigation fix-ups can still race with OpenIddict’s token persistence. Keeping the ignore rule for OpenIddictToken in AbpEfCoreNavigationHelper remains a valid fix for 9.3.2.
- Additional hardening (optional):
- If you also see concurrency logs for OpenIddictAuthorization, you can extend the guard to skip that type too:
using Volo.Abp.OpenIddict.Authorizations; // ... if (e.Entry.Entity is OpenIddictToken || e.Entry.Entity is OpenIddictAuthorization) { return; }- Ensure you’re not manually attaching OpenIddict entities in your code.
- Verify your DB isolation and background jobs aren’t updating OpenIddict tables during token issuance.
Given your findings, keep your AbpEfCoreNavigationHelper replacement in place. It’s the correct, minimal, and safe solution for the concurrency you’re observing.
- Sources
- https://abp.io/support/questions/8917/VoloAbpDataAbpDbConcurrencyException-The-database-operation-was-expected-to-affect-1-rows
- https://abp.io/support/questions/8068/Suspicious-updates-on-OpenIddictApplication-when-token-endpoint-is-called-with-clientcredentials-granttype#answer-3a158dd3-918a-fd76-1e37-5cfe204100e5
- https://abp.io/support/questions/9298/ABP-9-Upgrade---login-screen-409-error
- https://abp.io/docs/latest/framework/architecture/modularity/basics
This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.
-
0
hi
You can add
OpenIddictTokentoIgnoredNavigationEntitySelectorsinstead of overridingAbpEfCoreNavigationHelper.Configure<AbpEntityChangeOptions>(options => { options.IgnoredNavigationEntitySelectors.Add("DisableOpenIddictToken", type => type == typeof(OpenIddictToken)); });