Optimistic locking is favorable for smallish objects. However, for larger objects, optimistic locking poses a problem. A user might invest a lot of work into modifying an object before learning that he cannot save his work, because he lost the race for committing first. Fortunately, adding pessimistic locking to the optimistic locking at hand is possible.
Manipulating time-stamps does NOT work
A naive approach is to manipulate the timestamps of a loaded object in such a fashion that it looks as if it had been written immediately after loading. This does not work, however, because subsequent loads will not be impressed by the time-stamp. Other users can read and write the object to their hearts' content, and the user with the original load will have difficulties saving his modifications. This is NOT pessimistic locking.
Method 1: extra flag, extra storage provider
The most obvious route to pessimistic locking is an extra field (flag) in the lockable domain object. Upon load (
DomainObject.OnLoaded method), the flag is set and subsequent modifications are impossible if that flag is set. In theory, checking the flag could be done by the application, i.e. some layer supertype with appropriate overloads of
DomainObject.OnLoaded. Keep in mind, however, that the object you want to load (and lock) might be loaded in the course of a transaction that has already modified other objects. Since you can't commit single objects, only transactions, a commit for writing the lock field will commit the modifications for all the other objects. This is not desirable. Opening the object in another root-transaction for writing the lock is not possible either, because at the time your overridden
OnLoaded is invoked, your object is part of a sub-transaction already. Trying to load it into another root transaction will give you a concurrency violation exception.
The correct approach is to leave locking and monitoring the lock to the storage provider. If you want to go the route with the extra flag, write your own storage provider for the objects you want to lock pessimistically. With your dedicated storage provider, locking is fairly easy. In your storage provider you CAN override the loader in such a fashion that it updates and checks your locked flag.
Method 2: lock objects
A simpler, but more expensive method is to provide extra lock objects for the domain objects you want to lock pessimistically. Such domain objects contain a reference property to the lock object, also a domain object. If the lock object (i.e. a property in it) is set to true, no modifications are possible.
In contrast to the object you want to lock, the lock object is never loaded for anything other than maintaining the flag. It is never loaded implicitely. The lock object attached to a particular domain object CAN be loaded into its own root transaction and committed without affecting other objects. This method does not require a special storage provider, and is probably less work and easier to use. However, the extra
Commit() round-trip is more expensive than maintaining and monitoring the flag data in a dedicated storage provider – the method preferred by the ECMS team, by the way. (ECMS is a rubicon rubicon.