2012-11-23 22 views
5

我写了一个.NET + EF应用程序。一切工作正常,在一个单一的线程。多线程 - 这是另一回事。实体框架并发刷新和更新

在我的EF对象中,我有一个整数计数器。此属性被标记为“并发模式=固定”。基本上,我想要做的是更新这个计数器的几个线程。 像这样操作:

this.MyCounter -= 1; 

因为它的并发模式已更改为“固定”,当我特林更新这已经更改属性 - 一个OptimisticConcurrencyException被抛出。

为了解决这个并发问题,我使用这个代码:

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); 
    } 
} 

这段代码的结果是一个无限(或也许它只是看起来像)循环。每调用一次this.UsageAmount -= 1就会抛出一个OptimisticConcurrencyException,这会导致循环再次运行。

我的EntityContainer.Instance.Entities是提供EF上下文PER THREAD的单例类。这意味着每个线程都有独特的上下文。代码:

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,调用Entities.Refresh方法之后 - 它看起来像它的工作(对象状态是不变,属性格式值是在数据库中究竟存在)。

我该如何解决这个并发问题?

+1

你能张贴代码你提到'EntityContainer.Instance.Entities'的单身类? - 只是你提供的部分“每个线程的EF上下文”。 – lukiffer

+0

EntityContainer.Instance.Entities代码被插入到帖子中。 – No1Lives4Ever

+0

当你说*每个*调用抛出时,这是否可以归结为某些线程被阻止进行更新,因为它们每次都被打到数据库?数据库中的值是否真的在变化? – shambulator

回答

1

我在一些代码中解决了这个问题,我使用一个信号量为我的数据库中的信号量写了一个多实例azure webrole。这是我用来获取信号量的代码。我不得不添加一些额外的代码来处理我的竞争实例之间发生的竞争条件。我还添加了一个时间释放,以防我的信号因某些错误而被锁定。

 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 ... 

我的信号模式是这样的:

using System.ComponentModel.DataAnnotations; 

public class Semaphore : MyEntityBase //contains audit properties 
{ 

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

    public string Description { get; set; } 
}