2
 database.RunInTransaction(() =>
      {            
            if (dbVersion < DatabaseConstants.DATABASE_VERSION)
            {
                OnUpgrade();
              }
    });
        database.Commit();        
    }


  public void onUpgrade(){
     //inserting list of person
     database.RunInTransaction(() =>
           {
            //insert(TableNamePerson,PersonData)
            });
      database.Commit();


     database.RunInTransaction(() =>
        {
          //insert(TableNameContacts,ContactData)
       });
       database.Commit();        
    }

ネストされたトランザクションを使用しているときに、次の例外が発生します。

savePoint は無効です。SaveTransactionPoint への呼び出しの結果である必要があります

4

1 に答える 1

0

ドキュメントとコードで見たものから、意図した動作である可能性があります。Action指定された forはRunInTransaction、トランザクション内に全体としてカプセル化されます。SQLite は一度に 1 つのトランザクションしかサポートしないため、トランザクションが存在しない場合は新しいトランザクションが作成され、そうでない場合は、既に実行中のトランザクション内にセーブ ポイントが作成されます。

つまり、コミットを呼び出す必要はなく、それを行うことは許可されていません。RunInTransactionお世話になります。これは 1 つのブロックでは正常に機能しますが、コードのようにネストされたブロックでは一貫性のない動作が発生RunInTransactionします。これはバグだと思います。

例外が発生した場合、トランザクション全体がロールバックされて終了します。つまり、正常に終了したブロックでさえロールバックされます! 明示的または暗黙的なステートメントの後のすべてのステートメントBEGIN はなくなりました。内部例外をキャッチした場合でも、すべての外部RunInTransactionブロックが ArgumentExceptions をスローします。

savePoint is not valid, and should be the result of a call to SaveTransactionPoint

より細かく制御したい場合は、明示的なセーブ ポイントを使用する必要がありRollbackToますRollback

何が起こるかを説明するために、単体テストを作成しました。

[Test]
public void InsertItemIntoTable()
{
    using (var connection = Container.Resolve<IDatabase>().GetConnection())
    {
        var item1 = new Item { Id = 1, Description = "Test 1", Text = "Text for test 1" };
        var item2 = new Item { Id = 2, Description = "Test 2", Text = "Text for test 2" };
        var countAtStart = connection.Query<Item>("SELECT * FROM Item").Count;

        connection.RunInTransaction(() => // transaction started
        { 
            var saveTransactionPoint = connection.SaveTransactionPoint();
            connection.Insert(item2);
            // would fail as a commit would finish the transaction inside the action:
            // connection.Commit();

            // works as the transaction does not yet end
            connection.RollbackTo(saveTransactionPoint);
            Assert.IsTrue(connection.IsInTransaction);
        });
        Assert.IsFalse(connection.IsInTransaction);

        try
        {
            connection.RunInTransaction(() => // transaction started
            {
                connection.Insert(item1);
                var countAfter1stInsert = connection.Query<Item>("SELECT * FROM Item").Count;
                Assert.AreEqual(countAtStart + 1, countAfter1stInsert);
                connection.RunInTransaction(() => { connection.Insert(item2); });
                var countAfter2ndInsert = connection.Query<Item>("SELECT * FROM Item").Count;
                Assert.AreEqual(countAtStart + 2, countAfter2ndInsert);
                // bad SQL statement provokes an exception: no such table: bar. 
                try
                {
                    connection.RunInTransaction(() => // new save point within running transaction 
                    {
                        connection.Execute("SELECT foo FROM bar", "will throw exception");
                    });
                }
                catch (Exception e)
                {
                    // the whole transaction was rolled back already 
                    Assert.IsFalse(connection.IsInTransaction);
                    // that is why the outer block will fail next
                }
            });
        }
        catch (Exception e)
        {
            // outer RunInTransaction could not release its own save point and crashes with:
            // "savePoint is not valid, and should be the result of a call to SaveTransactionPoint"
        }

        var countAfterRollback = connection.Query<Item>("SELECT * FROM Item").Count;
        Assert.AreEqual(countAtStart, countAfterRollback);

        Assert.IsFalse(connection.IsInTransaction);
        // new transaction point start a deferred transaction as no transaction is running
        var point1 = connection.SaveTransactionPoint();
        Assert.IsTrue(connection.IsInTransaction);
        connection.Insert(item1);
        var point2 = connection.SaveTransactionPoint();
        connection.Insert(item2);
        var point3 = connection.SaveTransactionPoint();
        connection.Execute("INSERT INTO 'Item'('Id','Text','Description') VALUES (100,'Test 100',NULL);");
        connection.RollbackTo(point3);
        connection.RollbackTo(point2);
        // will commit the first insert i.e. item1, which implictily began a transaction
        connection.Commit();
        Assert.IsFalse(connection.IsInTransaction);
        var afterFinalRollback = connection.Query<Item>("SELECT * FROM Item").Count;
        // thus item1 has made it to the database
        Assert.AreEqual(countAtStart + 1, afterFinalRollback);
        // but not for ever ;)
        connection.Execute("delete from item where id > 0");
    }

}
于 2018-02-15T14:40:53.510 に答える