1.數據併發控制(Data Concurrency Control)簡介數據庫
數據併發控制(Data Concurrency Control)是用來處理在同一時刻對被持久化的業務對象進行屢次修改的系統。當多個用戶修改業務對象的狀態並試圖併發地將其持久化到數據庫時,須要一種機制來確保一個用戶不會對另外一個併發用戶的事務狀態形成負面影響。
有兩種形式的併發控制:樂觀和悲觀。樂觀併發控制假設當多個用戶對業務對象的狀態同時進行修改時不會形成任何問題,也稱爲最晚修改生效(last change wins)。對於一些系統,這是合理的行爲。但若是業務對象的狀態須要與從數據庫中取出的狀態保持一致,就須要悲觀併發控制。
悲觀併發控制能夠有多中風格,能夠在檢索出記錄後鎖定數據表,也能夠保存業務對象原始內容的副本,而後再進行更新以前將該副本與數據存儲中的版本進行比對。確保在此次事務期間沒有對該記錄進行修改。併發
2.數據併發控制的實現示例ide
經常使用的數據併發控制實現方式有兩種:數據庫實現及代碼控制實現。悲觀併發控制在數據庫實現方面能夠有加入數據庫鎖機制,代碼控制實現方面能夠增長一個保存版本號字段,用於版本之間的對比。使用版本號來檢查在業務實體從數據庫中檢索出以後是否被修改。更新時,把業務實體的版本號與數據庫中的版本號進行比對以後再提交修改。這樣確保業務實體在被檢索出後沒有被修改。ui
使用版本號來實現悲觀併發控制的方式,其中的版本號能夠使用數據庫中提供的數據類型timestamp或代碼中控制管理版本。數據庫timestamp類型字段在每一次update操做均會生成新值。this
1>、timestamp版本控制spa
SQL Server中timestamp類型對應C#的byte[]類型,timestamp字段值爲byte[8]。版本控制
2>、程序代碼實現版本控制code
代碼結構:orm
EntityBase.cs對象
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DataAccessPatterns.DataConcurrencyControl.Model { public abstract class EntityBase { public Guid Version { get; set; } } }
Person.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace DataAccessPatterns.DataConcurrencyControl.Model { public class Person : EntityBase { public Guid ID { get; set; } public string FirstName { get; set; } public string LastName { get; set; } } }
IPersonRepository.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DataAccessPatterns.DataConcurrencyControl.Model; namespace DataAccessPatterns.DataConcurrencyControl.Repository { public interface IPersonRepository { void Add(Person person); void Save(Person person); Person FindBy(Guid id); } }
PersonRepository.cs
using System; using System.Collections.Generic; using System.Linq; using System.Text; using DataAccessPatterns.DataConcurrencyControl.Model; namespace DataAccessPatterns.DataConcurrencyControl.Repository { public class PersonRepository : IPersonRepository { public void Add(Person person) { using (var context = new DataAccessPatternsContext()) { context.Persons.Add(person); context.SaveChanges(); } } public void Save(Person person) { // person.Version爲獲取出來的上一次版本,Version字段值保持獲取出來時字段值。 string strSql = String.Format(@"UPDATE dbo.Person SET FirstName='{0}',LastName='{1}' WHERE ID='{3}' AND Version='{4}'", person.FirstName, person.LastName, person.ID, person.Version); using (var context = new DataAccessPatternsContext()) { int affectedRows = context.Database.ExecuteSqlCommand(strSql, null); if (affectedRows == 0) { throw new ApplicationException(@"No changes were made to Person ID (" + person.ID + "), this was due to another process updating the data."); } else { // person.Version賦予新值用於下一次版本對比 person.Version = Guid.NewGuid(); } } } public Person FindBy(Guid id) { using (var context = new DataAccessPatternsContext()) { return context.Persons.Find(id) as Person; } } } }