Entity Framework Extensions Concurrency (EF Core)
Problem
Your model has a concurrency property, and you must resolve optimistic concurrency using a pattern.
Concurrency exceptions happens on:
- BulkSaveChanges
- BulkUpdate
- BulkDelete
- BulkMerge
BulkSaveChanges
When a concurrency error happens, only the first entry in error is returned (exactly like SaveChanges).
There are three possible scenarios:
- Database Wins (You keep database values)
- Custom Resolution (You merge properties from database and client entity)
- Client Wins (You keep current entity values)
Database Wins && Custom Resolution
If you want to keep database values or use a custom resolution (choose if you keep the current value or database value depending on the property) use a solution similar to this one:
var saved = false; while (!saved) { try { // Attempt to save changes to the database context.BulkSaveChanges(); saved = true; } catch (DbBulkOperationConcurrencyException ex) { foreach (var t in ex.Entries) { var entry = context.Entry(t); if (entry.Entity is EntitySimple) { var proposedValues = entry.CurrentValues; var databaseValues = entry.GetDatabaseValues(); foreach (var property in proposedValues.Properties) { var proposedValue = proposedValues[property]; var databaseValue = databaseValues[property]; // CHOOSE which value should be written to database proposedValues[property] = databaseValue; } // Refresh original values to bypass next concurrency check entry.OriginalValues.SetValues(proposedValues); } } } }
For this solution ensures that your concurrency property value is updated with the database value.
Client Wins
If you wish to keep the current value, there is two choice:
- Only update the concurrency property with the database value (See Database Wins && Custom Resolution solution)
- Use the
AllowConcurrency = false
option. When enabled, theBulkSaveChanges
will no longer check if there is some concurrency error.
context.BulkSaveChanges(options => options.AllowConcurrency = false);
BulkUpdate / BulkDelete / BulkMerge
Due to backward compatibility, in EF Core, the concurrency is not enabled by default for those operations.
To enable it, you need to use the EnableConcurrencyForBulkOperation = true
option:
context.BulkUpdate(list, options =>
{
options.EnableConcurrencyForBulkOperation = true;
});
When a concurrency error happens, the method will return a DbBulkOperationConcurrencyException
, which contains all error entries.
Again there are three possible scenarios:
- Database Wins (You keep database values)
- Custom Resolution (You merge properties from database and client entity)
- Client Wins (You keep current entity values)
You can use the same logic as BulkSaveChanges
to handle concurrency issues, depending on how you want to handle entities in error.
ZZZ Projects