2012-11-23 5 views
5

Ho scritto un'applicazione .NET + EF. Tutto funziona bene su un singolo thread. Su più thread: è un'altra storia.Entity Framework aggiornamento e aggiornamento della concorrenza

Nel mio oggetto EF ho un contatore intero. Questa proprietà è contrassegnata come "Concurrency Mode = Fixed". Fondamentalmente, quello che sto cercando di fare è aggiornare questo contatore su diversi thread. Ti piace questa operazione:

this.MyCounter -= 1; 

Perché è la modalità concorrenza è stata modificata in "Fixed", quando sto Tring per aggiornare una proprietà che è già cambia - un OptimisticConcurrencyException viene generata.

Al fine di risolvere questi problemi di concorrenza, sto usando questo codice:

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

Il risultato di questo codice è un ciclo infinito (o forse il suo aspetto proprio come). Ogni chiamata di this.UsageAmount -= 1 genera un OptimisticConcurrencyException, che fa ricominciare il ciclo.

My EntityContainer.Instance.Entities è una classe singleton che fornisce un contesto EF PER FILO. Ciò significa che ogni thread ha un contesto unico. Il codice:

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, dopo aver chiamato i metodi Entities.Refresh - sembra che tutto funzioni (stato oggetto è invariato e il valore propery è esattamente ciò che esiste nel database).

Come posso risolvere questo problema di concorrenza?

+1

Potresti inserire il codice per la classe Singleton lei ha citato 'EntityContainer.Instance.Entities'? - Solo la parte in cui stai fornendo "un contesto EF per thread". – lukiffer

+0

Il codice EntityContainer.Instance.Entities viene inserito nel post. – No1Lives4Ever

+0

Quando si pronuncia * ogni * richiamo di chiamata, questo potrebbe essere impedito ad alcuni thread di fare aggiornamenti perché vengono battuti sul database ogni volta? Il valore nel DB sta effettivamente cambiando? – shambulator

risposta

1

Ho risolto questo problema in un codice che ho scritto per un'ibridi multi-istanza utilizzando un semaforo che ho salvato nel mio database. Ecco il codice che uso per ottenere il semaforo. Ho dovuto aggiungere del codice extra per gestire le condizioni di gara che si verificano tra le mie istanze in competizione. Aggiungo anche un rilascio nel caso in cui il mio semaforo si blocca bloccato a causa di un errore.

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

Il mio modello semaforo si presenta così:

using System.ComponentModel.DataAnnotations; 

public class Semaphore : MyEntityBase //contains audit properties 
{ 

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

    public string Description { get; set; } 
}