В c# compact-framework, как вы выполняете транзакцию SQLite, сохраняя ограничения внешнего ключа в открытом соединении? - PullRequest
0 голосов
/ 05 марта 2020

Я создаю приложение в 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
}


1 Ответ

0 голосов
/ 05 марта 2020

Вы можете включить внешние ключи во время транзакций. Вы просто не можете включить или выключить их во время транзакций - вы должны включить их перед началом транзакции.

Вероятно, проще всего включить прагму, когда вы открываете соединение - и тогда вы можете забыть о это.

Это прекрасно работает для меня:

// 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);
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...