No1Lives4Ever
No1Lives4Ever

Reputation: 6903

Entity Framework concurrency refresh and update

I have written a .NET + EF application. Everything works fine on a single thread. On multiple threads - it's another story.

In my EF object I have an integer counter. This property is marked as "Concurrency Mode = Fixed". Basically, what I'm trying to do is update this counter on several threads. Like this operation:

this.MyCounter -= 1;

Because it's concurrency mode has been changed to "Fixed", when I'm tring to update a property that's already change - an OptimisticConcurrencyException is thrown.

In order to solve this concurrency problems, I'm using this code:

while (true)
{
    try
    {
        this.UsageAmount -= 1; // Change the local EF object value and call SaveChanges().
        break;
    }
    catch (OptimisticConcurrencyException)
    {
        Logger.Output(LoggerLevel.Trace, this, "concurrency conflict detected.");
        EntityContainer.Instance.Entities.Refresh(RefreshMode.StoreWins, this.InnerObject);
    }
}

The result of this code is an infinite (or maybe its just looks like) loop. Every call of this.UsageAmount -= 1 throw an OptimisticConcurrencyException, which causes the loop to run again.

My EntityContainer.Instance.Entities is a singleton class that provides an EF context PER THREAD. This means that every thread has a unique context. The code:

public sealed class EntityContainer
    {
        #region Singlethon Implemantation
        private static Dictionary<Thread, EntityContainer> _instance = new Dictionary<Thread,EntityContainer> ();
        private static object syncRoot = new Object();
        public static EntityContainer Instance
        {
            get
            {
                if (!_instance.ContainsKey(Thread.CurrentThread))
                {
                    lock (syncRoot)
                    {
                        if (!_instance.ContainsKey(Thread.CurrentThread))
                            _instance.Add(Thread.CurrentThread, new EntityContainer());
                    }
                }
                return _instance[Thread.CurrentThread];
            }
        }
        private EntityContainer()
        {
            Entities = new anticopyEntities2();
        }
        #endregion

        anticopyEntities2 _entities;
        public anticopyEntities2 Entities
        {
            get
            {
                //return new anticopyEntities2();
                return _entities;
            }
            private set
            {
                _entities = value;
            }
        }
    }

BTW, after calling the Entities.Refresh methods - it looks like it's working (object state is Unchanged and the propery value is exactly what exists in the database).

How can I solve this concurrency problem?

Upvotes: 5

Views: 2560

Answers (1)

Ben Tidman
Ben Tidman

Reputation: 2139

I solved this in some code that I wrote for a multi-instance azure webrole by using a semaphore that I save in my database. Here's the code I use to get the semaphore. I had to add in some extra code to handle the race condition that happens between my competing instances. I also add in a time release in case my semaphore gets stuck locked because of some error.

        var semaphore = SemaphoreRepository.FetchMySemaphore(myContext);
        var past = DateTime.UtcNow.AddHours(-1);

        //check lock, break if in use.  Ignor if the lock is stale.
        if (semaphore == null || (semaphore.InUse && (semaphore.ModifiedDate.HasValue && semaphore.ModifiedDate > past)))
        {
            return;
        }

        //Update semaphore to hold lock
        try
        {
            semaphore.InUse = true;
            semaphore.OverrideAuditing = true;
            semaphore.ModifiedDate = DateTime.UtcNow;
            myContext.Entry(semaphore).State = EntityState.Modified;
            myContext.SaveChanges();
        }
        catch (DbUpdateConcurrencyException)
        {
            //concurrency exception handeling another thread beat us in the race.  exit
            return;
        }
        catch (DBConcurrencyException)
        {
            return;
        }

        //Do work here ...  

My semaphore model looks like this:

using System.ComponentModel.DataAnnotations;

public class Semaphore : MyEntityBase //contains audit properties
{

    [Required]
    [ConcurrencyCheck]
    public bool InUse { get; set; }

    public string Description { get; set; }
}

Upvotes: 1

Related Questions