如何處理Entity Framework中的DbUpdateConcurrencyException異常


1. Concurrency的作用

場景

有個修改用戶的頁面功能,我們有一條數據User, ID是1的這個User的年齡是20, 性別是female(數據庫中的原始數據)

正確的該User的年齡是25, 性別是male

 

這個時候A發現User的年齡不對, 就給改成25, 那么在Entity Framework中,我們會這樣做。

var user = dbConext.User.Find(1);

//B用戶在這里完成修改了User的性別

user.age = 25;

dbContext.SaveChanges();

 

但是加入在上面注釋處,有個B用戶發現性別不對,完成了對用戶性別的修改,改成male. 會出現什么結果呢。

var user = dbConext.User.Find(1);

當A執行這個代碼的時候,獲取的性別是female

user.age = 25;

當A執行這個代碼的時候, 不知道B已經修改了這個記錄的性別,這個時候A的user的性別還是female

dbContext.SaveChanges();

保存修改的時候,會把female覆蓋回去,這樣B用戶的修改就作廢了。

 

但這不是A的本意,A其實只是想修改年齡而已。

Entity Framework使用[ConcurrencyCheck] 來解決這種問題, 當標記為[ConcurrencyCheck] 的Entity屬性,如果發現在從數據庫中取下來和提交的時候不一致,就會出現DbUpdateConcurrencyException異常,避免錯誤提交。

 

2. 如何正確處理DbUpdateConcurrencyException異常

2.1 數據庫優先方式

原理是在出現異常的時候,重新加載數據庫中的數據,覆蓋Context本地數據

using (var context = new BloggingContext())
{
  var blog = context.Blogs.Find(1);
  blog.Name = "The New ADO.NET Blog";  
  bool saveFailed;
  do
  {
    saveFailed = false;
    try
    {
      context.SaveChanges();
    }
    catch (DbUpdateConcurrencyException ex)
    {
      saveFailed = true;
      // Update the values of the entity that failed to save from the store
      ex.Entries.Single().Reload();
    }
  } while (saveFailed);
}

 

2.2 客戶端優先方式

以Context保存的客戶端數據為主,覆蓋數據庫中的數據

using (var context = new BloggingContext())
{
  var blog = context.Blogs.Find(1);
  blog.Name = "The New ADO.NET Blog";
  bool saveFailed;
  do
  {
    saveFailed = false;
    try
    {
      context.SaveChanges();
    }
    catch (DbUpdateConcurrencyException ex)
    {
      saveFailed = true;
      // Update original values from the database
      var entry = ex.Entries.Single();
      entry.OriginalValues.SetValues(entry.GetDatabaseValues());
    }
  } while (saveFailed);
} 

 

3.3 綜合方式

有時候,不是非A即B的關系,我們希望綜合數據庫中的數據和context中修改的數據,再保存到數據庫中

使用下面的CurrentValues, GetDatabaseValues(), 得到Context數據和數據庫數據,重新構建一個正確的Entity,再更新到數據庫中

using (var context = new BloggingContext())
{
  var blog = context.Blogs.Find(1);
  blog.Name = "The New ADO.NET Blog";
  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);
}
public void HaveUserResolveConcurrency(DbPropertyValues currentValues, DbPropertyValues databaseValues, DbPropertyValues resolvedValues) {   // Show the current, database, and resolved values to the user and have   // them edit the resolved values to get the correct resolution. }

 

對上面方法的優化

使用DbPropertyValues總是別扭,使用Enttiy對象就會方便很多,下面就是轉換成Entity對象操作的方法

 

using (var context = new BloggingContext())
{
  var blog = context.Blogs.Find(1);
  blog.Name = "The New ADO.NET Blog";
  bool saveFailed;
  do
  {
    saveFailed = false;
    try
    {
      context.SaveChanges();
    }
    catch (DbUpdateConcurrencyException ex)
    {
      saveFailed = true;
      // Get the current entity values and the values in the database
      // as instances of the entity type
      var entry = ex.Entries.Single();
      var databaseValues = entry.GetDatabaseValues();
      var databaseValuesAsBlog = (Blog)databaseValues.ToObject();
      // Choose an initial set of resolved values. In this case we
      // make the default be the values currently in the database.
      var resolvedValuesAsBlog = (Blog)databaseValues.ToObject();
      // Have the user choose what the resolved values should be
      HaveUserResolveConcurrency((Blog)entry.Entity,
        databaseValuesAsBlog,
        resolvedValuesAsBlog);
      // Update the original values with the database values and
      // the current values with whatever the user choose.
      entry.OriginalValues.SetValues(databaseValues);
      entry.CurrentValues.SetValues(resolvedValuesAsBlog);
    }
  } while (saveFailed);
}
public void HaveUserResolveConcurrency(Blog entity,   Blog databaseValues,   Blog resolvedValues) { // Show the current, database, and resolved values to the user and have // them update the resolved values to get the correct resolution. }

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM