我们在开发项目的过程,总会遇到数据并发的情况。掌握好数据并发访问技术是保障数据库正确性、一致性所必需的。解决并发问题,可以从数据库方面下手,同时也可以从程序本身进行控制。我们现在就讨论,针对程序本身,如何控制好并发的现象。
XY-ORM平台提供了悲观并发和乐观并发两种解决方式。针对不同的应用,配置不同的解决方式。
关于悲观和乐观的概念,这边就不再详细描述,博客园高手也很多,呵呵。。
在XY-ORM中,要实现通过悲观并发控制,则是通过一种锁行为启用事务,在事务执行的过程中,阻止其他用户访问或者更新记录;
系统支持的锁行为有以下几种:
// 摘要:
// 指定连接的事务锁定行为。
public enum IsolationLevel
{
// 摘要:
// 正在使用与指定隔离级别不同的隔离级别,但是无法确定该级别。
Unspecified = -1,
//
// 摘要:
// 无法覆盖隔离级别更高的事务中的挂起的更改。
Chaos = 16,
//
// 摘要:
// 可以进行脏读,意思是说,不发布共享锁,也不接受独占锁。
ReadUncommitted = 256,
//
// 摘要:
// 在正在读取数据时保持共享锁,以避免脏读,但是在事务结束之前可以更改数据,从而导致不可重复的读取或幻像数据。
ReadCommitted = 4096,
//
// 摘要:
// 在查询中使用的所有数据上放置锁,以防止其他用户更新这些数据。防止不可重复的读取,但是仍可以有幻像行。
RepeatableRead = 65536,
//
// 摘要:
// 在 System.Data.DataSet 上放置范围锁,以防止在事务完成之前由其他用户更新行或向数据集中插入行。
Serializable = 1048576,
//
// 摘要:
// 通过在一个应用程序正在修改数据时存储另一个应用程序可以读取的相同数据版本来减少阻止。表示您无法从一个事务中看到在其他事务中进行的更改,即便重新查询也是如此。
Snapshot = 16777216,
}
通过XY-ORM来做悲观并发的方式如下:
// 首先,我们先创建一个基于用户的数据访问对象:
BaseDao<UserInfo> dao = new BaseDao<UserInfo>();
// 启用事务,并将事务的级别锁设定为RepeatableRead,可以防止其他用户更新数据
dao.GetSUDEoOption().AutoTransaction = true;
dao.GetSUDEoOption().IsolationLevel = System.Data.IsolationLevel.RepeatableRead;
// 加载刚才保存过的用户
UserInfo userLoad = dao.Load(string.Format("select {0} from {1} where {2}='{3}'", dao.Entity.ColumnNames, dao.Entity.TableName, "id", user.Id));
// 修改姓名,并更新到数据库
userLoad.Name = "李四";
dao.SaveOrUpdate(userLoad);
通过乐观并发控制,则是通过SQL语句进行控制。由于XY-ORM在更新、删除实体的时候,所产生的UPDATE SQL,DELETE SQL都是由ADO.NET的DbCommandBuilder产生的,控制这些sql语句的where约束都是通过ConflictOption属性达到我们的期望值。我们来看下这个属性的枚举值:
// 摘要:
// 指定将如何检测和解决对数据源的相互冲突的更改。
public enum ConflictOption
{
// 摘要:
// 更新和删除语句将在 WHERE 子句中包含表中的所有可搜索列。
CompareAllSearchableValues = 1,
//
// 摘要:
// 如果表中存在任何 Timestamp 列,则这些列在 WHERE 子句中用于所有生成的更新语句。
CompareRowVersion = 2,
//
// 摘要:
// 所有的更新和删除语句仅在 WHERE 子句中包含 System.Data.DataTable.PrimaryKey 列。如果未定义 System.Data.DataTable.PrimaryKey,则所有可搜索的列都包含在
// WHERE 子句中。这等效于 OverwriteChangesUpdate | OverwriteChangesDelete。
OverwriteChanges = 3,
}
祥亿平台将这个属性也封装到了BaseDao中,可以通过设置这个属性达到并发的粒度。
// 首先,我们先创建一个基于用户的数据访问对象:
BaseDao<UserInfo> dao = new BaseDao<UserInfo>();
// where 语句 只有包含主键字段
dao.GetSUDEoOption().OptimismConcurrent = System.Data.ConflictOption.OverwriteChanges;
// where 语句 包含主键字段以及任何timestamp字段
dao.GetSUDEoOption().OptimismConcurrent = System.Data.ConflictOption.CompareRowVersion;
// where 语句 包含全部字段
dao.GetSUDEoOption().OptimismConcurrent = System.Data.ConflictOption.OverwriteChanges;
// 加载刚才保存过的用户
UserInfo userLoad = dao.Load(string.Format("select {0} from {1} where {2}='{3}'", dao.Entity.ColumnNames, dao.Entity.TableName, "id", user.Id));
// 修改姓名,并更新到数据库
userLoad.Name = "李四";
dao.SaveOrUpdate(userLoad);
// 删除用户
dao.Delete(userLoad);
所以,基于事务以及sql控制的并发解决方案,祥亿平台都封装到了数据访问层中。
可能有些地方说的不是很明白,可以加入讨论群:222515272。希望大家给力!