在 c# compact-framework 中,如何在保持打开连接中的外键约束的同时执行 SQLite 事务?

In c# compact-framework, how do you do a SQLite transaction while keeping foreign key constraints in an open connection?

我正在 Windows Forms .Net Compact Framework 3.5 中创建一个带有后备 SQLite 数据库的应用程序。出于性能原因,我需要保持与数据库的开放连接,并且我需要使用事务来执行多个更新以实现回滚的可能性。

一些SQLite表中有外键约束,默认情况下外键强制是关闭的,所以我不得不在打开应用程序到数据库的连接时手动打开它们。

现在的问题是事务不允许外键约束: (https://sqlite.org/pragma.html#pragma_foreign_keys)

所以当我 运行 更新语句时,它们失败并且事务回滚。

我尝试在交易前手动关闭外键约束,然后更新成功。但是当我尝试在交易后重新打开外键约束时,更新失败了。

我需要打开外键约束,但它们在交易期间失败,我不能简单地关闭它们,运行 一个交易,然后再打开它们。那我该怎么办呢?

using (var cmd1 = new SQLiteCommand(Con))
{
    cmd1.CommandText = "PRAGMA foreign_keys = 0"; //turn foreign key contraint off before transaction
    cmd1.ExecuteNonQuery();                      
}

var cmd2 = new SQLiteCommand(Con);
using (SQLiteTransaction transaction = Con.BeginTransaction())
{
    //run update commands here

    transaction.Commit();                       //run transaction
}

using (var cmd3 = new SQLiteCommand(Con))
{
    cmd3.CommandText = "PRAGMA foreign_keys = 1"; //turn foreign key constraint back on after transaction
    cmd3.ExecuteNonQuery();                      //this doesnt work
}


您可以在交易期间启用外键。您只是无法在交易期间打开或关闭它们 - 您必须在开始交易之前打开它们。

打开连接时打开 pragma 可能是最简单的方法 - 然后您就可以忘记它了。

这对我来说很好用:

// Con is a SQLite connection, that has been opened.    
using (var cmd = Con.CreateCommand())
{
    cmd.CommandText = "PRAGMA foreign_keys = ON";
    cmd.ExecuteNonQuery();
}

using (var cmd = Con.CreateCommand())
{
    cmd.CommandText = @"
    CREATE TABLE A(id integer not null primary key);
    CREATE TABLE B(id integer not null primary key, a_id integer, FOREIGN KEY(a_id) REFERENCES A(id))
    ";
    cmd.ExecuteNonQuery();
}

try
{
    using (var transaction = Con.BeginTransaction())
    {
        using (var cmd = Con.CreateCommand())
        {
            cmd.CommandText = "INSERT INTO A VALUES(1); INSERT INTO A VALUES(2); INSERT INTO B VALUES(1, NULL); INSERT INTO B VALUES(2, 1);";
            cmd.ExecuteNonQuery();
        }

        using (var cmd = Con.CreateCommand())
        {
            cmd.CommandText = "INSERT INTO B VALUES(3, 55);";
            cmd.ExecuteNonQuery();  //Will crash beceause of invalid reference
        }

        transaction.Commit();
    }
} catch(DbException) {
    // Ignore transaction failure in this example   
    if(!e.Message.Contains("foreign key constraint failed"))
        throw;
}

using (var cmd = Con.CreateCommand())
{
    // Validate that nothing was inserted in the failed transaction
    cmd.CommandText = "SELECT COUNT(*) FROM A";
    Debug.Assert((long)cmd.ExecuteScalar() == 0);
}