在 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);
}
我正在 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);
}