SQLite Transaction不提交

时间:2009-11-18 16:14:49

标签: c# sqlite transactions commit

对于我们正在开发的应用程序,我们需要从表中读取 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;

如果我删除了该交易,一切都按预期工作。 “域内容”是域特定的,而不是从记录集读取值不访问数据库。我忘记了一步吗?

2 个答案:

答案 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可序列化合同(不包括严重错误; - )

快乐编码


请参阅Locking in SQLiteSQLite pragmasAtomic Commit in SQLite