System.Data.SQLite 的嵌套事务与保存点
一组扩展方法,使 System.Data.SQLite 能够访问保存点
引言
SQLite 本身不允许嵌套事务。但是,它确实允许使用中间保存点标记事务,这在一定程度上模拟了嵌套事务。解释所有这些工作原理的文档可以在这里^找到。
背景
尽管可以在没有当前事务的情况下直接在数据库连接上发出 SAVEPOINT 命令,但这样做会隐式地创建一个事务并改变 RELEASE
命令的工作方式。为了减轻任何混淆,该模块的设置使得只能在事务的上下文中创建保存点。因此,对数据库的最终更新只有在发出 COMMIT
时才会被写入,并且整个事务将在发出 ROLLBACK
时中止,而不管事务中任何保存点的释放或回滚。这与事务的一般工作方式一致(无论是否有保存点)。
Using the Code
所需的所有代码都在一个 .cs 文件中。与其将其放入类库中,不如将该文件包含到您的项目中并开始使用它(尽管您可以根据需要将其构建到单独的库中)。事实上,所有代码都存在于一个 static
扩展类中。就扩展的使用而言,没有特殊的对象。
大多数保存点的操作都是在 SQLiteTransaction
对象的扩展方法中处理的。为 SQLiteTransaction
对象定义了四个扩展方法。还有两个 BeginTransaction
扩展方法用于 SQLiteConnection
对象,它们接受一个额外的 string
savepointName
参数,并自动将命名的保存点添加到新创建的事务中。
重要的是要注意,所有保存点名称都不区分大小写,并且不需要是唯一的。对保存点的任何释放或回滚都将作用于具有匹配名称的最新一个。
为 SQLiteTransaction
对象定义的四个扩展方法是
void AddSavepoint(string savepointName)
此方法,顾名思义,在事务中创建一个新的保存点。
void ReleaseSavepoint(string savepointName)
我认为关于其工作原理的最佳描述是 SQLite 文档中的第二个定义
RELEASE 的另一种观点是,它将一个命名的事务合并到其父事务中,这样命名的事务及其父事务就成为同一个事务。在 RELEASE 之后,命名的事务及其父事务将一起提交或回滚,无论它们的命运如何。
这意味着保存点内的所有尚未回滚的命令都有效地附加到紧接在保存点之前的命令中,并且已释放的保存点不再存在于事务中。任何跟随正在释放的保存点且先前尚未释放或回滚的保存点也将在此时间自动释放。
void RollbackToSavepoint(string savepointName)
此方法将回滚在命名的保存点之后发出的所有命令,即使这些命令包括先前已释放的保存点。但是,命名的保存点本身仍然存在。在回滚后发出的对事务的任何命令仍被认为是该保存点的一部分。
void RollbackAndRelease(string savepointName)
此命令封装了连续发出的回滚和释放。以这种方式发出时,事务将返回到紧接在创建命名的保存点之前的状态。
关注点
执行 SQLiteTransaction.Commit()
、SQLiteTransaction.Rollback()
或关闭或处置底层连接会自动从事务中释放保存点列表。这通过在连接的 Commit
、Rollback
、StateChange
和 Disposed
事件上附加事件处理程序来实现。但是,我确实通过实践发现,在处置列表时从列表中删除这些事件处理程序很重要。因为事件附加到连接对象上,所以在该连接上创建的新事务正在删除的列表中触发事件。
我还了解到,堆栈枚举器从最后一个项目到第一个项目(即项目的删除顺序,而不是它们的输入顺序)。