对于我们正在开发的应用程序,我们需要从表中读取 n 行,然后根据域特定条件选择性地更新这些行。在此操作期间,需要锁定数据库的所有其他用户以避免错误读取。
我开始一个事务,读取行,并在迭代记录集时构建一串更新语句。读完记录集后,关闭记录集并运行更新。此时我提交了事务,但是没有对数据库执行任何更新。
private static SQLiteConnection OpenNewConnection()
{
try
{
SQLiteConnection conn = new SQLiteConnection();
conn.ConnectionString = ConnectionString;//System.Configuration.ConfigurationManager.AppSettings["ConnectionString"];
conn.Open();
return conn;
}
catch (SQLiteException e)
{
LogEvent("Exception raised when opening connection to [" + ConnectionString + "]. Exception Message " + e.Message);
throw e;
}
}
SQLiteConnection conn = OpenNewConnection();
SQLiteCommand command = new SQLiteCommand(conn);
SQLiteTransaction transaction = conn.BeginTransaction();
// Also fails transaction = conn.BeginTransaction();
transaction = conn.BeginTransaction(IsolationLevel.ReadCommitted);
command.CommandType = CommandType.Text;
command.Transaction = transaction;
command.Connection = conn;
try
{
string sql = "select * From X Where Y;";
command.CommandText = sql;
SQLiteDataReader ranges;
ranges = command.ExecuteReader();
sql = string.Empty;
ArrayList ret = new ArrayList();
while (MemberVariable > 0 && ranges.Read())
{
// Domain stuff
sql += "Update X Set Z = 'foo' Where Y;";
}
ranges.Close();
command.CommandText = sql;
command.ExecuteNonQuery();
// UPDATES NOT BEING APPLIED
transaction.Commit();
return ret;
}
catch (Exception ex)
{
transaction.Rollback();
throw;
}
finally
{
transaction.Dispose();
command.Dispose();
conn.Close();
}
return null;
如果我删除了该交易,一切都按预期工作。 “域内容”是域特定的,而不是从记录集读取值不访问数据库。我忘记了一步吗?
答案 0 :(得分:4)
当你在transaction.Commit()行上设置断点时,你看到它被击中了吗?
最终答案:
SQLite的锁定不像你假设看到的那样http://www.sqlite.org/lockingv3.html。鉴于此,我认为您有一个事务范围问题,可以通过重新组织代码来轻松解决:
string selectSql = "select * From X Where Y;";
using(var conn = OpenNewConnection()){
StringBuilder updateBuilder = new StringBuilder();
using(var cmd = new SQLiteCommand(selectSql, conn))
using(var ranges = cmd.ExecuteReader()) {
while(MemberVariable > 0 && ranges.Read()) {
updateBuilder.Append("Update X Set Z = 'foo' Where Y;");
}
}
using(var trans = conn.BeginTransaction())
using(var updateCmd = new SQLiteCommand(updateBuilder.ToString(), conn, trans) {
cmd.ExecuteNonQuery();
trans.Commit();
}
}
答案 1 :(得分:2)
有关SQLite中交易的帖子/答案中的一些评论的附加说明。这些适用于使用日记的SQLite 3.x,可能适用于或不适用于不同的配置 - WAL略有不同但我不熟悉它。有关确切信息,请参阅locking in SQLite。
SQLite中的所有事务都是SERIALIZABLE(有关一个小例外,请参阅read_uncommitted
编译指示)。 new 读取不会阻塞/失败除非写入进程已启动(保持有EXCLUSIVE / PENDING锁定)并且写入将在所有未完成的读取之前启动是完整的,它可以获得一个EXCLUSIVE锁(WAL不是这样,但事务隔离仍然是相同的。)
即上面的整个序列在代码中不是原子,并且可以读取序列(A) - >读(B) - >写(A) - >读(B),其中A和B表示不同的连接(想象在不同的线程上)。在读取(B)时,即使存在中间写入,数据仍然是一致的。
要使代码序列本身成为原子,需要lock
或类似的同步机制。或者,可以使用SQLite本身通过使用{exclusive}的locking_mode
pragma创建锁定/同步。但是,即使上面的代码不是原子,数据也会遵守SQL可序列化合同(不包括严重错误; - )
快乐编码