Блокировка базы данных SQLite при подключении - PullRequest
0 голосов
/ 31 октября 2019

У меня есть база данных SQLite, которая сохраняется в сетевой папке и может быть доступна нескольким пользователям. Теперь необходимо изменить схему этой базы данных (самостоятельно написанной программой), и мне нужно убедиться, что во время операции обновления никто другой не сможет открыть базу данных (ни для записи, ни для чтения).

Насколько мне известно, PRAGMA locking_mode=EXCLUSIVE может быть использован для блокировки базы данных. К сожалению, эксклюзивная блокировка получается только при выполнении первой операции записи.
Это означает, что во время между открытием базы данных, установкой режима блокировки и первой операцией записи другой пользователь сможет открыть базу данных.

Есть ли способ получить эксклюзивную блокировку при открытии базы данных с System.Data.SQLite из C #?

EDIT Как вы просиликод, здесь вы идете:

void UpdateDatabaseSchema(Boolean UpdateNeeded)
{
    // make sure that all SQLite* objects are disposed correctly by using-statement, otherwise database will not be closed correctly!
    using (var Connection = new SQLiteConnection("./Database.db"))
    using (var Command = Connection.CreateCommand())
    {
        Connection.Open();
        Command.CommandText = "PRAGMA locking_mode=EXCLUSIVE;";
        Command.ExecuteNonQuery();
        Command.CommandText = "PRAGMA locking_mode;";
        using (var DataReader = Command.ExecuteReader())
        {
            while (DataReader.Read())
            {
                var Test = DataReader.GetString(0);
            }
        }
        if (UpdateNeeded)
        {
            if (System.Windows.Forms.MessageBox.Show("Do you want to update the database schema?", "Update needed", System.Windows.Forms.MessageBoxButtons.YesNo) == System.Windows.Forms.DialogResult.Yes)
            {
                Command.CommandText = "CREATE TABLE Users (Test TEXT NOT NULL DEFAULT 0);";
                Command.ExecuteNonQuery();
            }
        }
    }
}

Очевидно, что чтение режима блокировки доступно только для отладки (и будет удалено для производительного кода).
Теперь, что произойдет, если другой пользователь откроет ту же базу данных? - чуть-чуть - позже, чем первый пользователь, но быстрее нажимает «Да»? Первый пользователь получит сообщение об ошибке, потому что схема уже была изменена.
Да, я мог бы написать утверждение по-другому, но это обновление является лишь примером, в будущем могут быть и будут более сложные запросы, и я не будуне хочу заботиться об этих условиях гонки в каждой команде (по крайней мере, если это возможно).
Следовательно, мне нужно заблокировать базу данных при открытии .

@ C Perkins:
- Решение этой проблемы на основе файловой системы - это то, о чем я не задумывался. Я рассмотрю эту возможность, спасибо за ввод!
- Онлайн-документы также упоминают: «Когда пишется база данных в первый раз,эксклюзивный замок получен и удерживается. "Я знаю, что оно освобождается только при закрытом соединении, проблема только в том, что получено при первой операции записи.

1 Ответ

0 голосов
/ 15 ноября 2019

Мое тестирование показывает, что монопольную блокировку получают, выполняя любую команду изменения базы данных, независимо от того, вносит ли она изменения. Другими словами, следующие две команды в итоге получат эксклюзивную блокировку, но WHERE false делает команду недействительной.

//* The following only changes the mode, but does not lock the file
Command.CommandText = "PRAGMA locking_mode=EXCLUSIVE;";
Command.ExecuteNonQuery();

try {
    using (var cmdLock = Connection.CreateCommand())
    {
        //* The following command will force an exclusive file lock to be obtained.
        //* Although 'WHERE false' will cause the actual UPDATE to fail,
        //*   the actual statement is valid SQL and will not cause an error. 
        cmdLock .CommandText = "UPDATE Users SET Test = 'bogus' WHERE false;";
        cmdLock .ExecuteNonQuery();
    }
    //* Exclusive lock obtained
    //... free to do updates
} 
catch {
   MessageBox.Show("Failed to obtain exclusive lock, try again later.", "Lock failed")
}


...