创建连接 + 交易:

public SQLiteTransaction BeginTransaction()
{
            var con = new SQLiteConnection(@"Data Source=A:\TransactionScopeTest\TransactionTest.db;Foreign Keys=ON");
            con.Open();
            var trans = con.BeginTransaction();
            return trans;
}

使用相同的主键值执行 2 个 sqlite 插入以引发异常
[TestMethod]
public void TestMethod1()
{
    using (var trans = BeginTransaction())
    {
        try
        {
            SQLiteConnection con = trans.Connection;

            SQLiteCommand cmd1 = con.CreateCommand();
            cmd1.CommandText = "INSERT INTO TEST(Name) VALUES('John')";
            cmd1.ExecuteNonQuery();

            SQLiteCommand cmd2 = con.CreateCommand();
            cmd2.CommandText = "INSERT INTO TEST(Name) VALUES('John')";
            cmd2.ExecuteNonQuery();

            trans.Commit();
        }
        catch (Exception)
        {
            trans.Rollback();
            throw;
        }
    }
}

当我使用 SQLite 时,它​​的最佳实践是为每个执行的 sql 命令使用 SQLiteTransaction 类。
来自事务的连接需要在数据提供者方法之间共享。

我现在问你多个问题:

1.) 当由于插入相同的主键“John”而发生 SQLiteException 时,不会插入任何“John”值。没关系,因为我使用了一个事务并且 .Commit() 必须被执行。困扰我的是为什么我在 catch 块中使用 trans.Rollback() 没有任何区别。

2.) 我正在使用“using(resource)”语句,那么如果事务成功/提交到连接状态会发生什么?它会被关闭吗?只是担心我不使用 `using(var trans = new SQLiteTransaction()){...}

最佳答案

回答您的问题:

  • 事务必须像 Daniel 所说的那样明确提交。在意外错误情况下,我宁愿我的数据保持原样,而不是处于半提交状态,这是事务的重点。在这种情况下,catch 块可用于重试具有不同参数等的操作。在我工作的许多情况下,如果事务在没有提交的情况下到达 using 语句的末尾,它将回滚它,而无需我编写显式的 try/catch。请记住,在几乎所有异常情况下, using 块中的对象仍将被释放,即使您没有捕获异常。 (我喜欢这种方法,因为代码更清晰,没有到处尝试/捕获 - 我只在可以做出相应 react 时才使用 try/catch)
  • using 语句没问题。如果事务已提交,则不会回滚任何内容。如果事务尚未提交,则事务将被回滚。但请记住,处理事务对象不会显式关闭底层数据库连接。

  • 不过,我注意到的一件事是,您创建的命令对象是 而不是与事务关联的 。如果要针对 SQL Server 或 Oracle 执行此代码,则会抛出异常,指出必须将所有命令分配给事件事务(如果有)。

    要将命令与事务相关联,在创建每个新命令对象后,您需要以下代码:
    cmd.Transaction = trans;
    

    通常我的数据库代码遵循以下格式:
    using (SqlConnection connection = new SqlConnection("...")) {
      connection.Open();
      using (SqlTransaction transaction = connection.BeginTransaction())
      using (SqlCommand command = connection.CreateCommand()) {
        command.Transaction = transaction;
        command.CommandText = "INSERT INTO ...";
        // add parameters...
        command.ExecuteNonQuery();
        transaction.Commit();
      }
      // Reference to question 1: At this point in the code, assuming NO unhandled
      // exceptions occurred, the connection object is still open and can be used.
      // for example:
      using (SqlCommand command = connection.CreateCommand()) {
        command.CommandText = "SELECT ...";
        using (SqlDataReader reader = command.ExecuteReader()) {
          while (reader.Read()) {
            // do awesome processing here.
          }
        }
      }
    }
    

    上述连接流将确保在发生异常时清除所有与连接、事务和命令对象相关的资源。如果抛出异常,则错误在抛出它的行上,而不是在捕获并再次抛出它的 catch 块上。此外,事务将被回滚并且底层数据库连接将被关闭(或返回到池中,如果存在的话)。

    请记住,如果某个东西具有 Dispose() 方法并实现了 IDisposable 接口(interface),最好将其包装在 using 语句中,因为即使现在调用 Dispose() 什么都不做,也不能保证将来会如此。

    关于c# - 虽然我没有回滚我的事务,但 SQLite 事务没有成功?,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/7334312/

    10-12 17:14
    查看更多