Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Entity Framework Optimistic Concurrency Exception not occuring

We have an ASP.Net MVC application that uses EF4 as its data access layer and we're seeing unexpected behaviour with regards to OptimisitcConcurrencyExceptions not being thrown when we think they should be.

We have simplified the problem down to the following code...

   using System.Linq;
    using Project.Model;

    namespace OptimisticConcurrency
    {
        class Program
        {
            static void Main()
            {
                Contact firstContact = null;
                using (var firstEntities = new ProjectEntities())
                {
                    firstContact = (from c in firstEntities.Contacts 
                       where c.LastName == "smith" select c).Single();
                }

                using (var secondEntities = new ProjectEntities())
                {
                    var secondContact = (from c in secondEntities.Contacts 
                       where c.LastName == "smith" select c).Single();

                    secondContact.Title = "a";
                    secondEntities.SaveChanges();
                }

                firstContact.Title = "b";

                using (var thirdEntities = new ProjectEntities())
                {
                    var thirdContact = (from c in thirdEntities.Contacts 
                       where c.LastName == "smith" select c).Single();

                    thirdContact.Title = firstContact.Title;

                    //EXPLICITLY SET VERSION HERE
                    thirdContact.Version = firstContact.Version;  

                    thirdEntities.SaveChanges();
                }
            }
        }
    }

This is a rather simple version of what happens in our MVC app, but the same problem occurs.

When we call SaveChanges on the thirdEntities, I expect the exception and nothing is being thrown.

Much more interestingly, when we attach the SQL Profiler, we see that the Version is being used in the where clause but it is thirdEntities Version value (the current one in the DB) being used, not the firstEntities values DESPITE it being explicitly set immediately before SaveChanges is called. SaveChanges is resetting the Version to be the retrieved value not the set value.

In the EDMX, the Version is set to have a StoreGeneratedPattern is set to Computed.

Anyone have any idea what is going on here?

like image 913
Colin Desmond Avatar asked Mar 16 '11 15:03

Colin Desmond


People also ask

How do you handle optimistic concurrency exceptions?

To resolve optimistic concurrency conflicts, you can take advantage of the Reload method to update the current values in your entity residing in the memory with the recent values in the database. Once reloaded with the updated data, you can attempt to persist your entity again in the database.

How do you configure entity framework for optimistic concurrency?

If you do want to implement this approach to concurrency, you have to mark all non-primary-key properties in the entity you want to track concurrency for by adding the ConcurrencyCheck attribute to them. That change enables the Entity Framework to include all columns in the SQL WHERE clause of UPDATE statements.

How do you implement optimistic concurrency?

You can easily implement optimistic concurrency in any database by using a timestamp or a version number. Then, when a user is trying to commit a change to the database, you can compare the version number or the timestamp and decide which record to keep.


1 Answers

This is a problem. Once the column is set to Computed you can't set its value in the application (you can but the value is not used).

Edit:

If you load entity from database it is by default tracked with the context. The context stores its original values. Original values are for example used for snapshot change tracking but they are also used as the only valid source of Computed properties. If you set Computed property in your entity the value is not used and original value is used insted. The workaround is to modify original value (before you modify anything else):

using (var context = new TestEntities())
{
    var entityToUpdate = context.MyEntities.Single(e => e.Id == someId);
    entityToUpdate.Timestamp = entity.Timestamp;

    ObjectStateEntry entry = context.ObjectStateManager.GetObjectStateEntry(entityToUpdate);
    entry.ApplyOriginalValues(entityToUpdate);

    // set modified properties
    context.SaveChanges();
}

Edit 2:

Btw. once you have both actually loaded timestamp and previously retrieved timestamp you can simply compare them in your application instead of doing it in the database.

like image 188
Ladislav Mrnka Avatar answered Sep 30 '22 11:09

Ladislav Mrnka