数据访问模式:数据并发控制(Data Concurrency Control)
1.数据并发控制(Data Concurrency Control)简介
数据并发控制(Data Concurrency Control)是用来处理在同一时刻对被持久化的业务对象进行多次修改的系统。当多个用户修改业务对象的状态并试图并发地将其持久化到数据库时,需要一种机制来确保一个用户不会对另一个并发用户的事务状态造成负面影响。
有两种形式的并发控制:乐观和悲观。乐观并发控制假设当多个用户对业务对象的状态同时进行修改时不会造成任何问题,也称为最晚修改生效(last change wins)。对于一些系统,这是合理的行为。但如果业务对象的状态需要与从数据库中取出的状态保持一致,就需要悲观并发控制。
悲观并发控制可以有多中风格,可以在检索出记录后锁定数据表,也可以保存业务对象原始内容的副本,然后再进行更新之前将该副本与数据存储中的版本进行比对。确保在这次事务期间没有对该记录进行修改。
2.数据并发控制的实现示例
常用的数据并发控制实现方式有两种:数据库实现及代码控制实现。悲观并发控制在数据库实现方面可以有加入数据库锁机制,代码控制实现方面可以增加一个保存版本号字段,用于版本之间的对比。使用版本号来检查在业务实体从数据库中检索出之后是否被修改。更新时,把业务实体的版本号与数据库中的版本号进行比对之后再提交修改。这样确保业务实体在被检索出后没有被修改。
使用版本号来实现悲观并发控制的方式,其中的版本号可以使用数据库中提供的数据类型timestamp或代码中控制管理版本。数据库timestamp类型字段在每一次update操作均会生成新值。
1>、timestamp版本控制
SQL Server中timestamp类型对应C#的byte[]类型,timestamp字段值为byte[8]。
2>、程序代码实现版本控制
代码结构:
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; } } } }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· 阿里巴巴 QwQ-32B真的超越了 DeepSeek R-1吗?
· 【译】Visual Studio 中新的强大生产力特性
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 【设计模式】告别冗长if-else语句:使用策略模式优化代码结构
2012-07-29 TinyMCE下载及使用