1

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

如果我删除交易,一切都会按预期进行。“域的东西”是特定于域的,除了从记录集中读取值之外,它不会访问数据库。我忘了一步吗?

4

2 回答 2

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();
    }
}   
于 2009-11-18T17:59:59.397 回答
2

关于这篇文章/答案中关于 SQLite 中事务的一些评论的附加说明。这些适用于使用 Journaling 的 SQLite 3.x,可能适用于也可能不适用于不同的配置 - WAL略有不同,但我不熟悉它。有关最终信息,请参阅SQLite 中的锁定。

SQLite 中的所有事务都是SERIALIZABLE的(请参阅read_uncommitted编译指示了解一个小例外)。的读取不会阻塞/失败,除非写入过程已经开始(持有一个 EXCLUSIVE/PENDING 锁)并且在所有未完成的读取完成并且它可以获得一个 EXCLUSIVE 锁之前写入不会开始(这不是真的对于WAL,但事务隔离仍然相同)。

也就是说上面的整个序列在代码中不会是原子的,序列可能是 read(A) -> read(B) -> write(A) -> read(B),其中 A 和 B 代表不同的连接(想象在不同的线程上)。在两次读取(B)中,即使中间有写入,数据仍然是一致的。

为了使代码序列本身具有原子lock性,需要一种或类似的同步机制。或者,可以使用 SQLite 本身通过使用locking_mode“exclusive”编译指示创建锁定/同步。但是,即使上面的代码不是原子的,数据也会遵守 SQL 可序列化合约(不包括一个严重的错误 ;-)

快乐编码


请参阅SQLite 中的锁定SQLite 编译指示SQLite 中的原子提交

于 2011-03-20T01:41:11.927 回答