Entity Framework 樂觀併發處理數據庫
有一段時間沒有更新博客了,今天終於有一些時間,和你們討論一個Entity Framework 樂觀併發處理的問題。首先須要說明的是,這裏提到的 「併發」 並非指的多線程處理,也就是筆者這裏要討論的是另一個問題場景,這個場景描述以下:api
1. 系統用戶A從數據庫中取得一條記錄Record-1多線程
2. 系統用戶B從數據庫中取得同一條記錄Record-1併發
3. 系統用戶B修改記錄Record-1,並保存到數據庫ui
4. 系統用戶A修改記錄Record-1,並保存到數據庫this
這個場景裏面的問題是系統用戶B對數據路記錄Record-1的修改被A的修改沖掉了,這種行爲在某些業務場景中是正確的,可是在某些場景中可能會有問題,由於這樣的場景中A可能但願感知Record-1的變化,並作出相應的代碼策略處理。針對這種場景Entity Framework 專門有一種解決方案,Timestamp或者叫Rowersion,具體實現代碼以下, 在須要支持樂觀併發的model上增長Timestamp字段,字段的名字叫RowVersion,固然對應的數據庫表上也要加上這個字段RowVersion,類型是timestamp, 這樣在以上描述的場景發生時,系統就會捕獲到相應的異常,從而有機會對這種場景進行相應的業務處理。spa
若是不喜歡Data Annotation,也可使用 Fluent api 進行標記,具體代碼以下,線程
modelBuilder.Entity<Department>().Property(p => p.RowVersion).IsRowVersion();
namespace ContosoUniversity.Models { public class Department {
[ConcurrencyCheck] public int DepartmentID { get; set; } [StringLength(50, MinimumLength = 3)] public string Name { get; set; } [DataType(DataType.Currency)] [Column(TypeName = "money")] public decimal Budget { get; set; } [DataType(DataType.Date)] [DisplayFormat(DataFormatString = "{0:yyyy-MM-dd}", ApplyFormatInEditMode = true)] [Display(Name = "Start Date")] public DateTime StartDate { get; set; } public int? InstructorID { get; set; } [Timestamp] public byte[] RowVersion { get; set; } public virtual Instructor Administrator { get; set; } public virtual ICollection<Course> Courses { get; set; } } }
try
{ db.Entry(departmentToUpdate).OriginalValues["RowVersion"] = rowVersion; await db.SaveChangesAsync(); return RedirectToAction("Index"); } catch (DbUpdateConcurrencyException ex) { var entry = ex.Entries.Single(); var clientValues = (Department)entry.Entity; var databaseEntry = entry.GetDatabaseValues(); if (databaseEntry == null) { ModelState.AddModelError(string.Empty, "Unable to save changes. The department was deleted by another user."); } else { var databaseValues = (Department)databaseEntry.ToObject(); if (databaseValues.Name != clientValues.Name) ModelState.AddModelError("Name", "Current value: " + databaseValues.Name); if (databaseValues.Budget != clientValues.Budget) ModelState.AddModelError("Budget", "Current value: " + String.Format("{0:c}", databaseValues.Budget)); if (databaseValues.StartDate != clientValues.StartDate) ModelState.AddModelError("StartDate", "Current value: " + String.Format("{0:d}", databaseValues.StartDate)); if (databaseValues.InstructorID != clientValues.InstructorID) ModelState.AddModelError("InstructorID", "Current value: " + db.Instructors.Find(databaseValues.InstructorID).FullName); ModelState.AddModelError(string.Empty, "The record you attempted to edit " + "was modified by another user after you got the original value. The " + "edit operation was canceled and the current values in the database " + "have been displayed. If you still want to edit this record, click " + "the Save button again. Otherwise click the Back to List hyperlink."); departmentToUpdate.RowVersion = databaseValues.RowVersion; } }
固然Timestamp字段不是必須單獨添加的,也可使用model上的已有字段,假設這個字段是DepartmentID,咱們可使用ConcurrencyCheck註解進行標記,固然也但是使用Fluent api 進行標記,具體代碼以下,code
modelBuilder.Entity<Person>().Property(p => p.DepartmentID).IsConcurrencyToken();
總結orm
本文詳細描述了一種Entity Framework 樂觀併發處理的解決方案,而且經過Data Annotation和Fluent api 兩種方式進行了詳細的說明,但願對你們有所幫助。