In one of the microservices we noticed we were getting concurrency exceptions every time we updated a value object. And it wasn't a complicated nested value object, it was a value object made of two guids and two strings.
I spent a good afternoon trying to figure out why were we getting the error and could not see anything wrong in the code... and I thought... it's entity framework again!
I asked for help to the rest of the team and they found this thread Changes on Owned Entites Properties causes a concurrency conflict on same dbContext where they propose a workaround. We tried and it didn't work but we thought it was going in the right direction so we debug it and change it a bit and voilà the rowversion column was being updated again both in SQL and in my backend!
The modified code is this:
private void ConcurrencyFix() { var changedEntriesWithVos = ChangeTracker.Entries().Where(e => e.State == EntityState.Unchanged && e.References.Any(r => r.TargetEntry != null && (r.TargetEntry.State == EntityState.Modified || r.TargetEntry.State == EntityState.Added) && r.TargetEntry.Metadata.IsOwned() && e.Metadata.Relational().TableName == r.TargetEntry.Metadata.Relational().TableName)).ToArray(); foreach (var entry in changedEntriesWithVos) entry.State = EntityState.Modified; }
And we have placed it in our SaveChanges methods in our base context, from which all of our contexts are inheriting so we are sure this code is always being executed when we save.
public override int SaveChanges(bool acceptAllChangesOnSuccess) { ConcurrencyFix(); return base.SaveChanges(acceptAllChangesOnSuccess); } public override TaskSaveChangesAsync(bool acceptAllChangesOnSuccess, CancellationToken cancellationToken = default) { ConcurrencyFix(); return base.SaveChangesAsync(acceptAllChangesOnSuccess, cancellationToken); }
Have fun everyone!
No comments:
Post a Comment