Dapper.Contrib icon indicating copy to clipboard operation
Dapper.Contrib copied to clipboard

Dapper Snapshotter Updates "modified" Properties rather than "changed"

Open andy1547 opened this issue 7 years ago • 1 comments

Instead of Full Change tracking simply knowing which properties have had their setters called since the object was were returned by a Dapper would be a great feature.

If you retrieve a fresh object from the database and explicitly set a property, no matter what it should be issuing an update statement for that column when persisted . Dapper Snapshotter only looks at "modified" properties, instead of "changed" properties, therefore it may or may not issue and update for the column depending on original value held in memory. This is usually fine if concurrent connections are not updating the same columns or performing pessimist locking, however it can cause issues like: Client A fetches a column value of "Monday", Client B Updates the column to "Tuesday", then Client A sets the value on the object to "Monday" then performs an update, the outcome would be "Tuesday" even though Client A was last, this is because the Snapshotter thinks nothing has changed as the memory value is stale.

This causes unexpected results especially when implementing HTTP PUT requests that should replace the entire resource. A great explanation of this exists at: https://blog.jooq.org/2017/06/28/orms-should-update-changed-values-not-just-modified-ones/

Has anybody encountered any similar race conditions at scale? I was thinking of rolling my own Proxy implementation to keep track of calls made to setters as I can't see to find anything that suits my needs.

andy1547 avatar Jan 18 '18 18:01 andy1547

The usual solution in fully baked ORMs is to use versioning -- add a Version column, initially set to 0 or 1 on row creation. When updating the row, increment the Version but also condition it so the update fails unless the Version column has the same value as when the row was originally fetched. If it fails, then you have to re-fetch the row (with new Version) and reapply any updates that were intended (or just make it the user's problem to re-edit, depending on context).

uecasm avatar Jul 16 '19 02:07 uecasm