This is the 19th post in my ongoing series of Entity Framework Tips.
If you have a table with a timestamp column, and you reverse engineer an Entity from that table, you will end up with a Binary property in your entity (in my example called Version).
If you look at the properties window for that property you will see something like this:
The interesting thing here is the Concurrency Mode. The Entity Framework support 2 Concurrency Modes:
So timestamps have a Concurrency Mode of Fixed, which means the original value loaded from the database is included in the WHERE clause of any Updates or Deletes.
If you delve a little deeper by looking at the Storage Model (aka SSDL), which you can see if you open your EDMX in the XML editor, you will notice something else, namely that the Version property has a StoreGeneratedPattern of Computed.
There are 3 possible StoreGeneratedPatterns:
So because timestamps have a StoreGeneratedPattern of Computed, whenever the EF Inserts or Updates an entity with a timestamp column it will automatically get the latest timestamp value and feed it back into the Entity.
In the example below I have an Entity called Post that has a timestamp column called Version. Given that this simple code:
using (TipsEntities ctx1 = new TipsEntities()) { // Get a post (which has a Version Timestamp) in one context // and modify. Post postFromCtx1 = ctx1.Post.First(p => p.ID == 1); postFromCtx1.Title = "New Title"; // Modify and Save the same post in another context // i.e. mimicking concurrent access. using (TipsEntities ctx2 = new TipsEntities()) { Post postFromCtx2 = ctx2.Post.First(p => p.ID == 1); postFromCtx2.Title = "Newer Title"; ctx2.SaveChanges(); } // Save the changes... This will result in an Exception ctx1.SaveChanges(); }
… will cause an OptimisticConcurrencyException:
… now if we delve a little deeper, by clicking ‘View Detail’ you will see that the OptimisticConcurrencyException gives you access to the ObjectStateEntry(s)associated with the Entity(s) that caused the concurrency exception via the StateEntries property:
Which means that if you want to handle this sort of situation gracefully you simply trap the OptimisticConcurrencyException and grab the Entity(s) related to those StateEntries so you can provide some sort of message:
catch (OptimisticConcurrencyException ex) { ObjectStateEntry entry = ex.StateEntries[0]; Post post = entry.Entity as Post; Console.WriteLine("Failed to save {0} because it was changed in the database", post.Title); }
Pretty simple if you ask me.
Of course in the real world things are seldom this simple. Your requirements probably states that you have to give the user the ability to compensate and retry. How do you do that exactly?
Well that is definitely an interesting scenario, so expect another tip soon.