Reputation: 6903
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
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