O/R mappers and concurrency control
Paul Wilson and Alex Thissen both blog about concurrency control related to O/R mappers. Let me start by pointing you to an article about concurrency methods I wrote some time ago: Concurrency Control Methods: is there a silver bullet?. I don't believe in low level concurrency methods, as they give you the false sense of 'it has been taken care of', while they just don't do that: they still cause loss of work.
However, there are occasions where low level concurrency control can be helpful: for example if you don't care if someone looses his/her work and you just care about the consistent state of the database, or in situations where you for example want to prevent deletion of data after some period of time (user opens edit screen, goes away for lunch, comes back, deletes data, however the state of the record has been changed, the delete should not happen). This example also illustrates that concurrency control is not something that's solely related to saving data. It is also related to deleting data.
In my commercial O/R mapper LLBLGen Pro I had to solve the same question Paul Wilson had asked himself: how to implement concurrency control? History learns that when you ask a random group of developers: "how would you implement optimistic concurrency control?", you get a wide variety of answers. This means that, besides the point that there is no silver bullet to solve all concurrency problems, it is also not possible to define a common way to supply concurrency control: different developers will implement concurrency control differently, or at least: want to use it differently.
In LLBLGen Pro I've solved it by introducing a predicate factory which is used through the Strategy Pattern [GoF]. A predicate is a clause which can be used in a WHERE statement of a select or delete. The factory implements a common interface: IConcurrencyPredicateFactory, defined as follows:
public interface IConcurrencyPredicateFactory { IPredicateExpression CreatePredicate( ConcurrencyPredicateType predicateTypeToCreate, object containingEntity); }where ConcurrencyPredicateType is a normal Enum.
A developer who wants to add concurrency control to a given entity, can implement this interface and store an instance of that implementation in the entity instance. Once that is done, persistence logic, like the Save logic or Delete logic, also during recursive saves (saving a complete hierarchy of objects at once in a single transaction) will consult the IConcurrencyPredicateFactory instance for a predicate to add to the filter to be used in the Save or Delete action. Because the predicate is constructed at runtime, in a class the developer writes him/herself, the predicate can be constructed precisely how the developer wants it to be. The factory can even fire events, call delegates or consult other objects to retrieve information to produce the correct predicate expression, because the class is written by the developer, it just has to implement the one method defined in the interface.
Discussions how concurrency control has to be implemented are then not necessary anymore: the developer decides. Customer requires timestamp-based concurrency control and Order requires all-value-checking concurrency control? Just produce the predicate expression which matches those requirements: pass a different implementation of IConcurrencyPredicateFactory to the Customer entities than you pass to the Order entities. Concurrency control as flexible as you can possibly get, and usable not only on a per-type basis but also on a per-instance basis.