Concurrency Management with CodeFluent Entities
Since business applications are commonly used by multitudes of end users, concurrency management is a common issue in business application development.
Concurrency is a well-known issue and you’ll find nice articles (such as this one) explaining what concurrency is, as well as common patterns to address it.
To sum-up, there are three common ways to address concurrency management when developing your application:
- No concurrency protection
Probably, the easiest to implement :).
No concurrency protection implies several users can edit the same data, and the “last one wins” rule is applied.
In the optimistic concurrency model, users are always allowed to read the data, and perhaps even to update it. When the user attempts to save the data, however, the system checks to see if the data has been updated by anyone else since the user first retrieved it. If it has been changed, the update fails.
This concurrency model places locks on data. If one user has a record open and any other users attempt to read that data in a context that allows editing, the system denies the request.
CodeFluent Entities supports the optimistic concurrency protection model, and this concurrency protection is activated for all entities by default. However, having a concurrency protection doesn’t always make sense, may it be for business reasons or technical ones. In such cases, it’s then possible to disable concurrency at the project level:
<cf:project xmlns:cf="http://www.softfluent.com/codefluent/2005/1" defaultNamespace="Sample" defaultConcurrencyMode="None"> (...) </cf:project>
Or for a specific entity:
<Customer concurrencyMode="None"> (...) </Customer>
How it works
Entities with a concurrencyMode set to Optimistic (default) have an extra property named RowVersion that gets generated.
On each write operation, the RowVersion column in the persistence layer gets updated. Thanks to this property, CodeFluent can tell when a user tries to persist an obsolete version of an entity. This is done by comparing the persisted RowVersion to the one sent by the user: if ever they’re different, a CodeFluentConcurrencyException is raised.
Handling the CodeFluentConcurrencyException
When bumping into a CodeFluentConcurrencyException, the first question one should ask himself is: is it relevant to have concurrency protection on this business entity?
Having a concurrency protection doesn’t always make sense, may it be for business or technical reasons. For instance, a scenario where concurrency protection could be useless, would be in a case where children entities are automatically updated when a concurrency protected parent entity is updated: if updating the parent succeeded, updating children will.
Let’s get back to our exception and let’s say we do want concurrency protection on the entity that triggered the exception.
CodeFluent generates automatically a method named Reload which is made to help you handle those concurrency exceptions.
The Reload method takes as a parameter an enumeration named CodeFluentReloadOption which can be a combination of the following values:
Note: The Default value is actually set to Everything.
Thanks to this method, you’ll be able to handle your concurrency exception by calling the reload method with the right option. For instance, would you want to bypass the concurrency protection, you could reload just the RowVersion so that you can overwrite the current record.
On the other hand you could also reload you’re whole entity by calling this method and ask the user to start-over with his modifications.
CodeFluent Entities R&D team.