2017-06-22 98 views
0

我正在尋找一個優雅的解決方案來處理實體框架中的併發衝突。實體框架中記錄的併發訪問

我的用於服用藥物的表格有幾個欄目,如藥物名稱,劑量,頻率,給藥方法,註釋等等。操作員可以在查看記錄後修改劑量,頻率,管理方法等字段,也可以只添加註釋。

我們正在使用SQL Server數據庫。

我添加了timestamp類型的列LastUpdated。我正在使用此列來提高DbUpdateConcurrencyExceptionLastUpdated列中的ConcurrencyMode列已在實體設計器中設置爲Fixed

從我的控制器類的代碼片段:

// drugId and strNotes are passed from edit screen 
DataAccessLayer.Dal dal= new DataAccessLayer.Dal(); 

// Select record 
DrugDetail drugDetail = (from drugRow in dal.DrugDetails 
         where drugRow.id == drugId 
         select drugRow).ToList<DrugDetail>()[0]; 

// Assign notes 
drugDetail.Notes = strNotes; 

try 
{ 
    dal.SaveChanges(); 
} 
catch(DbUpdateConcurrencyException ex) 
{ 
    ((IObjectContextAdapter)dal).ObjectContext. 
      Refresh(System.Data.Objects.RefreshMode.StoreWins, drugDetail); 

    // Set the notes data again 
    drugDetail.Notes = strNotes; 

    // Is it possible that DbUpdateConcurrencyException occur again here 
    dal.SaveChanges(); 
} 

我擔心的是,當SaveChanges()從catch塊稱爲會再次出現併發衝突。我認爲有可能另一個用戶可以在刷新和保存在catch塊之間修改和保存相同的記錄。

我擔心有效嗎?我無法通過網上搜索找到任何具體的答案。

我正在考慮在我的測試環境中創建這個場景並驗證行爲,但是之後首先想到在這裏查看論壇。

感謝您的幫助。

按照要求DrugDetail類在這裏補充:可在任何時間發生

public partial class DrugDetail 
{ 
    public long id { get; set; } 
    public string DrugName { get; set; } 
    public Nullable<decimal> Dose { get; set; } 
    public Nullable<byte> Frequency { get; set; } 
    public string Method { get; set; } 
    public string Notes { get; set; } 
    public byte[] LastUpdated { get; set; } 
} 
+0

顯示DrugDetail類和配置(屬性和/或FluentApi) –

+0

順便說一句你想要SingleOrDefault()或Single()而不是ToList()[0] –

+0

只是一邊:如果可能'' .ToList ()[0];'將返回null,考慮將它改爲').ToList()。FirstOrDefault();' – Fabulous

回答

1

併發衝突。

使用循環重複該過程,直到您可以保存更改而沒有併發衝突。

DataAccessLayer.Dal dal= new DataAccessLayer.Dal(); 

//Select record 
DrugDetail drugDetail = (from drugRow in dal.DrugDetails 
         where drugRow.id == drugId 
         select drugRow).Single(); 

int count = 0; 

while (true) 
{ 
    //Assign notes 
    drugDetail.Notes = strNotes; 

    try 
    { 
     dal.SaveChanges(); 
     break; 
    } 
    catch(DbUpdateConcurrencyException) 
    { 
     count++; 
     if (count > 10) 
      throw; 

     ((IObjectContextAdapter)dal).ObjectContext. 
       Refresh(System.Data.Objects.RefreshMode.StoreWins, drugDetail); 

    } 
} 
+0

可以將drugDetail.Notes移出循環嗎? – Waleed

+0

@Waleed不適用於RefreshMode.StoreWins。查看文檔 –

+0

好的建議。我確信把SaveChanges()保存在一個循環中會使我能夠在每次嘗試中都跟蹤異常。謝謝。 – MKR

0

這聽起來像你正試圖自己養成你自己的樂觀併發機制。這可能不會奏效,因爲數據總是有可能發生變化,即使在您撥打電子郵件SaveChanges和SQL Server將數據寫入表格之間的微小時間內,這些數據仍在通過線路傳輸。

您需要特殊設置才能使其正常工作。如果您使用的是實體框架,你需要建立LASTUPDATED列作爲併發的道理,像這樣的東西:

modelBuilder.Entity<Department>().Property(p => p.LastUpdated).IsConcurrencyToken(); 

然後,你需要處理保存操作過程中引發的任何DbUpdateConcurrencyException

bool saveFailed; 
do 
{ 
    saveFailed = false; 
    try 
    { 
     context.SaveChanges(); 
    } 
    catch (DbUpdateConcurrencyException ex) 
    { 
     saveFailed = true; 

     // Get the current entity values and the values in the database 
     var entry = ex.Entries.Single(); 
     var currentValues = entry.CurrentValues; 
     var databaseValues = entry.GetDatabaseValues(); 

     // Choose an initial set of resolved values. In this case we 
     // make the default be the values currently in the database. 
     var resolvedValues = databaseValues.Clone(); 

     // Have the user choose what the resolved values should be 
     HaveUserResolveConcurrency(currentValues, databaseValues, resolvedValues); 

     // Update the original values with the database values and 
     // the current values with whatever the user choose. 
     entry.OriginalValues.SetValues(databaseValues); 
     entry.CurrentValues.SetValues(resolvedValues); 
    } 
} while (saveFailed); 

這樣做,這樣允許EF構造SQL以這樣的方式,該時間戳被檢查和該數據是相同的,原子事務內更新。

如果你堅持自己做,你可以將閱讀和保存包裝在你自己的交易中(一定要選擇合適的transaction isolation level)。但是,這樣做的效率會低得多,並且會延長行/頁/表被鎖定的時間,如果您有很多用戶,這會損害性能。

更多信息可查詢here

+0

我已經定義了「併發模式」=固定爲EDMX圖中的LastUpdated字段。我的理解是,EF將確保它不會使用LastUpdated數據的舊時間戳值更新表中的行。我會通讀你所建議的清單。非常感謝您向我展示方向。 – MKR