2012-11-23 9 views
5

Tôi đã viết một ứng dụng .NET + EF. Mọi thứ hoạt động tốt trên một sợi đơn. Trên nhiều chủ đề - đó là một câu chuyện khác.Thực thể Khung làm mới và cập nhật đồng thời

Trong đối tượng EF của tôi, tôi có bộ đếm nguyên. Thuộc tính này được đánh dấu là "Concurrency Mode = Fixed". Về cơ bản, những gì tôi đang cố gắng làm là cập nhật bộ đếm này trên một số chủ đề. Giống như hoạt động này:

this.MyCounter -= 1; 

Bởi vì nó chế độ đồng thời đã được đổi thành "cố định", khi tôi đang Tring để cập nhật một tài sản đó đã thay đổi - một OptimisticConcurrencyException được ném.

Để giải quyết vấn đề này đồng thời, tôi đang sử dụng mã này:

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

Kết quả của mã này là vô hạn (hoặc có thể nó chỉ trông giống như) vòng lặp. Mọi cuộc gọi của this.UsageAmount -= 1 ném một số OptimisticConcurrencyException, làm cho vòng lặp chạy lại.

My EntityContainer.Instance.Entities là một lớp đơn lẻ cung cấp ngữ cảnh EF PER THREAD. Điều này có nghĩa là mỗi luồng có một ngữ cảnh duy nhất. Mã này:

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, sau khi gọi Entities.Refresh phương pháp - có vẻ như nó hoạt động (trạng thái đối tượng là không thay đổi và giá trị propery là chính xác những gì tồn tại trong cơ sở dữ liệu).

Tôi làm cách nào để giải quyết vấn đề tương tranh này?

+1

bạn có thể đăng các mã cho lớp singleton bạn đề cập đến 'EntityContainer.Instance.Entities'? - Chỉ là phần mà bạn đang cung cấp "ngữ cảnh EF cho mỗi chủ đề". – lukiffer

+0

Mã EntityContainer.Instance.Entities được chèn vào bài đăng. – No1Lives4Ever

+0

Khi bạn nói * mọi cuộc gọi * ném, điều này có thể xảy ra với một số chủ đề bị ngăn không cho cập nhật vì chúng bị đánh vào cơ sở dữ liệu mỗi lần không? Giá trị trong DB thực sự có thay đổi không? – shambulator

Trả lời

1

Tôi đã giải quyết vấn đề này trong một số mã mà tôi đã viết cho một webrole azure đa dụ bằng cách sử dụng semaphore mà tôi lưu trong cơ sở dữ liệu của mình. Đây là mã tôi sử dụng để có được semaphore. Tôi đã phải thêm vào một số mã phụ để xử lý các điều kiện chủng tộc xảy ra giữa các trường hợp cạnh tranh của tôi. Tôi cũng thêm vào trong một thời gian phát hành trong trường hợp semaphore của tôi bị mắc kẹt bị khóa vì một số lỗi.

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

mô hình semaphore của tôi trông như thế này:

using System.ComponentModel.DataAnnotations; 

public class Semaphore : MyEntityBase //contains audit properties 
{ 

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

    public string Description { get; set; } 
}