sqlite - savePoint 无效,应该是调用 SaveTransactionPoint 的结果

标签 sqlite xamarin.forms sqlite.net

 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 的结果

最佳答案

从我在文档和代码中看到的内容来看,这可能是预期的行为。 ActionRunInTransaction 指定被整体封装在一个事务中。由于 SQLite 一次只支持一个事务,如果不存在则创建一个新事务,否则在已经运行的事务中创建一个保存点。

也就是说,您不必调用 commit,也不允许这样做。 RunInTransaction会照顾你的。这适用于一个 block 但与嵌套 RunInTransaction 有一些不一致的行为 block 就像你的代码一样。我会认为这是一个错误。

如果发生异常,则整个事务将回滚并因此终止。这意味着即使是那些成功完成的 block 也会被回滚!显式或隐式 BEGIN 之后的每个语句离开了。即使你已经捕获了内部异常,所有外部 RunInTransaction block 将抛出 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");
    }

}

关于sqlite - savePoint 无效,应该是调用 SaveTransactionPoint 的结果,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47301876/

相关文章:

c# - SQLite .NET,ExecuteScalarAsync<int>,如何知道什么时候没有结果?

database - SQLite 数据库方案作为实体关系模型

Python 和 sqlite3 - 添加数千行

xamarin.forms - 使用 xamarin 中的自定义渲染器更改条目边框颜色

c# - 命令和触发器之间的主要区别是什么? (Xamarin)

visual-studio - 让 Xamarin Forms Previewer 在 Visual Studio 中工作的正确方法?

php - 如何更新与 PHP 捆绑的 SQLite 版本

python - Sqlite3不从数据库读取

c# - 是否可以将 SQLite.NET 与不可变记录类型一起使用?