连接时锁定 SQLite 数据库
Lock SQLite database when connecting
我有一个 SQLite 数据库,它保存在网络共享上,可以被多个用户访问。
现在这个数据库的模式需要改变(通过一个自己编写的程序),我需要确保在更新操作期间没有其他用户可以打开数据库(无论是写还是读)。
据我所知,PRAGMA locking_mode=EXCLUSIVE
可用于锁定数据库。不幸的是排他锁只有在执行第一个写操作时才能获得。
这意味着在打开数据库、设置锁定模式和第一次写入操作之间的时间内,不同的用户将能够打开数据库。
有什么方法可以在 打开 数据库时使用 System.Data.SQLite 从 C# 获得独占锁?
编辑
当你请求一些代码时,你去吧:
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();
}
}
}
}
显然,锁定模式的读取仅用于调试(并且将被删除以用于生产代码)。
现在,如果另一个用户打开同一个数据库 - 比第一个用户晚一点 - 但点击 "Yes" 更快,会发生什么?第一个用户将收到错误消息,因为架构已更改。
是的,我可以用不同的方式写语句,但这个更新只是一个例子,将来可能 - 并且将会 - 更困难的查询,我不想在每个命令中关心这些竞争条件(至少如果可能的话) .
因此我需要在打开时锁定数据库。
@C 帕金斯:
- 在文件系统基础上处理问题是我没有考虑过的事情,我会研究这种可能性,感谢您的意见!
- 在线文档还提到 "The first time the database is written, an exclusive lock is obtained and held." 我知道它仅在连接关闭时释放,问题是它仅在第一次写入操作时 获得 。
我的测试表明,通过执行任何类型的数据库更改命令,无论它是否实际进行了更改,都可以获得独占锁。换句话说,以下两个命令最终将获得独占锁,但是 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")
}
我有一个 SQLite 数据库,它保存在网络共享上,可以被多个用户访问。
现在这个数据库的模式需要改变(通过一个自己编写的程序),我需要确保在更新操作期间没有其他用户可以打开数据库(无论是写还是读)。
据我所知,PRAGMA locking_mode=EXCLUSIVE
可用于锁定数据库。不幸的是排他锁只有在执行第一个写操作时才能获得。
这意味着在打开数据库、设置锁定模式和第一次写入操作之间的时间内,不同的用户将能够打开数据库。
有什么方法可以在 打开 数据库时使用 System.Data.SQLite 从 C# 获得独占锁?
编辑 当你请求一些代码时,你去吧:
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();
}
}
}
}
显然,锁定模式的读取仅用于调试(并且将被删除以用于生产代码)。
现在,如果另一个用户打开同一个数据库 - 比第一个用户晚一点 - 但点击 "Yes" 更快,会发生什么?第一个用户将收到错误消息,因为架构已更改。
是的,我可以用不同的方式写语句,但这个更新只是一个例子,将来可能 - 并且将会 - 更困难的查询,我不想在每个命令中关心这些竞争条件(至少如果可能的话) .
因此我需要在打开时锁定数据库。
@C 帕金斯:
- 在文件系统基础上处理问题是我没有考虑过的事情,我会研究这种可能性,感谢您的意见!
- 在线文档还提到 "The first time the database is written, an exclusive lock is obtained and held." 我知道它仅在连接关闭时释放,问题是它仅在第一次写入操作时 获得 。
我的测试表明,通过执行任何类型的数据库更改命令,无论它是否实际进行了更改,都可以获得独占锁。换句话说,以下两个命令最终将获得独占锁,但是 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")
}