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() 行にブレークポイントを設定すると、ヒットすることがわかりますか?

最終的な答え:

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 でのトランザクションに関するこの投稿/回答のコメントに関する追加メモ。これらはジャーナリングを使用する SQLite 3.x に適用され、さまざまな構成に適用される場合と適用されない場合があります。WALは少し異なりますが、私はそれに慣れていません。確実な情報については、SQLite でのロックを参照してください。

SQLite のすべてのトランザクションはSERIALIZABLE です( read_uncommitted1 つの小さな例外についてはプラグマを参照してください)。新しい読み取りは、書き込みプロセスが開始されてない限りブロック/失敗せず(EXCLUSIVE/PENDING ロックが保持されている)、すべての未処理の読み取りが完了して排他ロックを取得できるまで書き込みは開始されません (これは正しくありません)。ただし、トランザクションの分離は同じです)。

つまり、上記のシーケンス全体はコード内でアトミックではなく、シーケンスは read(A) -> read(B) -> write(A) -> read(B) である可能性があります。ここで、A と B は異なる接続を表します (想像してください)。別のスレッドで)。どちらの read(B) でも、間に書き込みがあったとしても、データは一貫しています。

コード自体のシーケンスをアトミックにするには、lockまたは同様の同期メカニズムが必要です。locking_modeまたは、 "exclusive" プラグマを使用して、SQLite 自体でロック/同期を作成することもできます。ただし、上記のコードがアトミックでなくても、データは SQL のシリアライズ可能なコントラクトに従います(重大なバグを除く ;-)

ハッピーコーディング


Locking in SQLiteSQLite プラグマ、およびAtomic Commit in SQLiteを参照してください。

于 2011-03-20T01:41:11.927 に答える