Entity Framework 4.1: Optimistic Concurrency (6)


This is part of a series of blog post about Entity Framework 4.1.  The past blog entries are:

In this article, I’ll cover optimistic concurrency.

Very often we need to manage many-users concurrency.  This is due to the speed at which humans interact with a machine compared to the speed machines interact with each other.  Typically, a human will use the better part of a minute (often more) to fill a data form while a machine transactions usually takes less than a second.

We can’t keep SQL transactions open on the server while a user is editing the corresponding form for many reasons:  contention, trust, technical reasons, etc.  .  For those reasons, we need to manage concurrency differently.  There are two big schools on concurrency management:  optimistic & pessimistic.  Pessimistic is typically more difficult since you need to implement some record-base locking mechanism and with it comes a lot problematic, such as how long should the system keep a lock before releasing it automatically.  Optimistic concurrency is easier.

The basic assumption of optimistic concurrency is that user changes rarely clashes.  This is true for applications with either few users or a lot of users but few users editing the same data at the same time.  The basic idea is that you give the service consumer a version of the data and when the consumer comes back to update, it gives you back that original version.  If the data changed in the back-end store and the version changed, the service can detect it and refuse the update.

Now the ‘version’ of the data can be different things.  It could be a version number associated to your data, it could be a ‘last modified date’ date-time field, it could be more than one field.  In Entity Framework, those are called concurrency token.  In this article, I’ll use SQL Server time-stamp feature:  this involves adding a column with the time-stamp type on the tables where we want to implement optimistic concurrency.  SQL Server takes care of updating this column each time a row is updated.

In order to tell Entity Framework that a property in an entity represents a concurrency token, you can either decorate the property with the ConcurrencyCheck attribute or use the model-builder.  I consider that concurrency tokens should be part of the model since they define business rules, so I’m going to use the attribute:

public class Order
{
    public int OrderID { get; set; }
    [Required]
    [StringLength(32, MinimumLength = 2)]
    public string OrderTitle { get; set; }
    [Required]
    [StringLength(64, MinimumLength=5)]
    public string CustomerName { get; set; }
    public DateTime TransactionDate { get; set; }
    [ConcurrencyCheck]
    [Timestamp]
    public byte[] TimeStamp { get; set; }

    public virtual List<OrderDetail> OrderDetails { get; set; }
    public virtual List<Employee> InvolvedEmployees { get; set; }
}

With this code in, optimistic concurrency will take place when we call SaveChanges on the DbContext.

The timestamp property type is byte[].  By marking the property with the attribute Timestamp, the mapping to SQL Server is done to a time-stamp SQL type column.

Now, let’s simulate optimistic problem.  The example here will go in three steps:

  • Create an order
  • Simulate the modification of the order by a user X
  • Simulate the modification of the order by the user Y, while the order has been modified already without Y’s knowledge

The way to simulate modification is to attach the entity to another Db-context, simulating service boundaries.  When we attach an entity to a db-context, it assumes the entity is unmodified, so we modify it afterwards. 

private static void ConcurrencyCheck()
{
    Order originalOrder;

    //    Create an order
    using (var context1 = new MyDomainContext())
    {
        originalOrder = new Order
        {
            OrderTitle = "Paper",
            CustomerName = "*Bob*",
            TransactionDate = DateTime.Now
        };

        context1.Orders.Add(originalOrder);
        context1.SaveChanges();
    }
    //    Simulate the modification of the created order by user X
    using (var context2 = new MyDomainContext())
    {    //    Recreate the order object in order to attach it
        var order = new Order
        {
            OrderID = originalOrder.OrderID,
            OrderTitle = originalOrder.OrderTitle,
            CustomerName = originalOrder.CustomerName,
            TransactionDate = originalOrder.TransactionDate,
            TimeStamp = originalOrder.TimeStamp
        };

        context2.Orders.Attach(order);

        //    Alter the order
        order.CustomerName = "Robert";

        context2.SaveChanges();
    }
    //    Simulate the modification of the created order by user Y (after user X already modified it)
    using (var context3 = new MyDomainContext())
    {    //    Recreate the order in order to attach it
        var order = new Order
        {
            OrderID = originalOrder.OrderID,
            OrderTitle = originalOrder.OrderTitle,
            CustomerName = originalOrder.CustomerName,
            TransactionDate = originalOrder.TransactionDate,
            TimeStamp = originalOrder.TimeStamp
        };

        context3.Orders.Attach(order);

        //    Alter the order
        order.CustomerName = "Luke**";

        try
        {
            context3.SaveChanges();
        }
        catch (DbUpdateConcurrencyException ex)
        {
            Console.WriteLine("Concurrency exception on " + ex.Entries.First().Entity.GetType().Name);
        }
    }
}

You could also force EF to believe the order is modified:

context3.Entry(order).State = EntityState.Modified;

So basically, EF supports optimistic concurrency out-of-the-box in a fairly simple form.  The trick is more around state management with EF:  when you attach entities to contexts, make sure they are in the right state:

  • Detached
  • Unchanged
  • Added
  • Deleted
  • Modified
About these ads

8 thoughts on “Entity Framework 4.1: Optimistic Concurrency (6)

  1. Pingback: Entity Framework 4.1: Inheritance (7) « Vincent-Philippe Lauzon's blog
  2. Pingback: Entity Framework 4.1: Bypassing EF query mapping (8) « Vincent-Philippe Lauzon's blog
  3. Pingback: Entity Framework 4.1 Series « Vincent-Philippe Lauzon's blog
  4. Pingback: Entity Framework 4.1 之六:乐观并发 » NoName
  5. Hi, and thanks for your helpful article. I have created my own ORM by deriving from DbContext, and I am a newbie to this. I have an article class and comment class with following definition :

    public partial class Article
    {
    public int ArticleID { get; set; }

    //other properties

    public virtual ICollection Comments { get; set; }
    }

    public partial class Comment
    {

    public int CommentID { get; set; }

    //other proprties

    public virtual Article Article { get; set; }

    }

    But when I want to delete some article and its associated comments by using following code, I got this error “Store update, insert, or delete statement affected an unexpected number of rows (0). Entities may have been modified or deleted since entities were loaded. Refresh ObjectStateManager entries.”

    var qry=from c in _articleRepo.GetArticleByID(id).Comments
    select c;

    foreach (var item in qry)
    {
    _commentRepo.DeleteComment(item.CommentID);

    _commentRepo.Save();
    }

    _articleRepo.DeleteArticle(id);

    _articleRepo.Save();

    would you please help me ?

  6. Hi,

    Well… I don’t know exactly what is inside your custom methods (GetArticleByID, DeleteComment, Save, etc.), but I’ll assume they do what their names imply and not more.

    The only thing I could see from the distance is that you load a comment directly and delete it in ‘DeleteComment’ which might bypass the ORM tracking system so that when you try to delete the article, it might try to cascade the delete on the comments that are no longer in the Database.

    That would be my best guess by not looking at the code. I haven’t tested cascade delete but I wouldn’t be surprise if it is implemented, hence you could probably skip the entire foreach loop and your code could work… I believe ;)

  7. Hi my code returns an OptimisticConcurrencyException instead of an DbUpdateConcurrencyException, do you know what would be the problem?

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s