{"record":{"id":"3a0da6b960c66d7d","repo":"aspnetboilerplate/aspnetboilerplate","slug":"ex-message-wraps-dbupdateconcurrencyexception","errorCode":null,"errorMessage":"ex.Message (wraps DbUpdateConcurrencyException)","messagePattern":"ex\\.Message \\(wraps DbUpdateConcurrencyException\\)","errorType":"exception","errorClass":"AbpDbConcurrencyException","httpStatus":null,"severity":"error","filePath":"src/Abp.EntityFrameworkCore/EntityFrameworkCore/AbpDbContext.cs","lineNumber":276,"sourceCode":"                    .Entity<TEntity>()\n                    .Property(property.Name)\n                    .HasConversion(dateTimeValueConverter);\n            });\n        }\n    }\n\n    public override int SaveChanges()\n    {\n        try\n        {\n            var changeReport = ApplyAbpConcepts();\n            var result = base.SaveChanges();\n            EntityChangeEventHelper.TriggerEvents(changeReport);\n            return result;\n        }\n        catch (DbUpdateConcurrencyException ex)\n        {\n            throw new AbpDbConcurrencyException(ex.Message, ex);\n        }\n    }\n\n    public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default(CancellationToken))\n    {\n        try\n        {\n            var changeReport = ApplyAbpConcepts();\n            var result = await base.SaveChangesAsync(cancellationToken);\n            await EntityChangeEventHelper.TriggerEventsAsync(changeReport);\n            return result;\n        }\n        catch (DbUpdateConcurrencyException ex)\n        {\n            throw new AbpDbConcurrencyException(ex.Message, ex);\n        }\n    }\n","sourceCodeStart":258,"sourceCodeEnd":294,"githubUrl":"https://github.com/aspnetboilerplate/aspnetboilerplate/blob/2323c13a152aa0ebfb37af3830f687d7f5b53795/src/Abp.EntityFrameworkCore/EntityFrameworkCore/AbpDbContext.cs#L258-L294","documentation":"When base.SaveChanges() raises DbUpdateConcurrencyException (an optimistic-concurrency conflict detected by EF Core), AbpDbContext catches it and rethrows as ABP's AbpDbConcurrencyException, carrying the original message and inner exception. This lets applications handle concurrency conflicts with a single ABP exception type.","triggerScenarios":"Calling SaveChanges() (or the surrounding repository/UoW save) on an AbpDbContext while another transaction modified the same row, so the UPDATE affects 0 rows and EF raises DbUpdateConcurrencyException.","commonSituations":"Two users editing the same entity concurrently; [Timestamp]/RowVersion concurrency tokens; stale entity loaded earlier and saved after a competing update; retry loops causing double writes.","solutions":["Catch AbpDbConcurrencyException at the application/service layer and inform the user to reload and re-apply changes","Reload the entity (repository refresh / re-query) and retry the change after a conflict","Add a [Timestamp] RowVersion column to enforce optimistic concurrency consistently","Use ABP's built-in concurrency handling in application services (AbpServiceBase handles it for AJAX responses)"],"exampleFix":"// before\n_repository.Update(order); // throws AbpDbConcurrencyException to caller unhandled\n// after\ntry\n{\n    _repository.Update(order);\n    await CurrentUnitOfWork.SaveChangesAsync();\n}\ncatch (AbpDbConcurrencyException)\n{\n    // reload entity, notify user, re-apply changes\n    await _repository.ReloadAsync(order);\n}","handlingStrategy":"try-catch","validationCode":"// detect likely conflict before save (optional re-check)\nvar entry = dbContext.Entry(entity);\nvar dbRowVersion = await dbContext.Set<TRowVersionHolder>()\n    .Where(x => x.Id == entity.Id)\n    .Select(x => (long?)x.RowVersion)\n    .FirstOrDefaultAsync();\nif (dbRowVersion.HasValue && dbRowVersion != entry.Property(\"RowVersion\").CurrentValue)\n{\n    // conflict already exists; reload entity instead of saving\n}","typeGuard":"bool isConcurrencyConflict(Exception ex)\n    => ex is AbpDbConcurrencyException || ex.InnerException is DbUpdateConcurrencyException;","tryCatchPattern":"try\n{\n    await CurrentUnitOfWork.SaveChangesAsync();\n}\ncatch (AbpDbConcurrencyException ex)\n{\n    Logger.Warn($\"Concurrency conflict on entity: {ex.Message}\");\n    await CurrentUnitOfWork.RollbackAsync();\n    throw new UserFriendlyException(L(\"ConcurrencyConflictMessage\"));\n}","preventionTips":["Add [Timestamp]/RowVersion columns to frequently updated entities","Keep entity instances short-lived; reload before editing in long sessions","Catch AbpDbConcurrencyException at the application service boundary and show a friendly message","Implement reload-and-retry logic for background jobs"],"tags":["efcore","abp","concurrency","optimistic-locking","savechanges"],"backgroundTag":"database-write-failed","analyzedSha":"2323c13a152aa0ebfb37af3830f687d7f5b53795","analyzedAt":"2026-09-08T08:00:50.638Z","contentChangedAt":"2026-09-08T08:00:50.638Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}