我正在尝试从我的 C# 应用程序中删除 SQLite 数据库文件。同一应用程序通过每次创建和处置一个新连接来对数据库执行多个 RW 查询。
当尝试删除数据库时,(当我可以确保程序没有查询数据库时,因此没有活动连接)我遇到了错误:
IOException: The process cannot gain access to <filename> because is being used by another process.
我已经调查了几个小时:我的代码,SO问题,使用procmon和resmon来确保我的进程是唯一持有该文件的活动句柄的进程。
完成所有这些后,我确定每当创建数据库连接时数据库文件都没有正确关闭。解释如下:
我有以下函数来执行查询并将结果加载到
DataTable
中:
public DataTable PerformQuery(string query) {
try {
DataTable table = new DataTable();
using(SQLiteConnection connection = new SQLiteConnection(connString)) {
SQLiteCommand cmd = connection.CreateCommand();
cmd.CommandText = query;
connection.Open();
if (!query.StartsWith("SELECT")) {
cmd.ExecuteNonQuery();
} else {
SQLiteDataReader reader = cmd.ExecuteReader();
FillTable(reader, table);
}
// Despite using the using scope, close and dispose the connection manually
connection.Close();
connection.Dispose();
}
// Kill all pools and call GC
SQLiteConnection.ClearAllPools();
GC.Collect();
GC.WaitForPendingFinalizers();
return table;
} catch (Exception ex) {
// Handle error... (not relevant)
return null;
}
}
好吧,在无限循环(每秒运行一次)中使用 SysInternals
handle.exe
并使用 Visual Studio 的调试器逐步调试程序,我发现我的数据库文件的文件句柄并未关闭,尽管:
Close
Dispose
using
范围我确实需要从代码内部删除我的数据库文件,但我不能,因为文件句柄从未关闭。
我该如何解决这个问题?
编辑1:
System.Data.SQLite
Pooling=false
添加到连接字符串中。您是否也尝试过关闭 SQLiteCommand?
using (SQLiteConnection connection = new SQLiteConnection(connString)) {
using (SQLiteCommand cmd = connection.CreateCommand()){
// do stuff here
}
}
SQLiteConnection.ClearAllPools();
GC.Collect();
GC.WaitForPendingFinalizers();
以下是我如何使用 Entity Framework 8 解决此问题,因为其他/较旧的解决方案对我不起作用:
async Task CloseDatabaseAsync()
{
try
{
await CloseSqliteDatabaseAsync();
}
catch(ObjectDisposedException)
{
// connection will be disposed, and fail due to invalid handle
}
}
async Task CloseSqliteDatabaseAsync()
{
await using var context = await _contextFactory.CreateDbContextAsync();
var conn = context.Database.GetDbConnection() as SqliteConnection;
conn.Open();
var result = SQLitePCL.raw.sqlite3_close_v2(conn.Handle);
conn.Handle.Close();
conn.Handle.Dispose();
GC.Collect();
GC.WaitForPendingFinalizers();
// delete or overwrite the database, whatever you need to do
File.Delete(filename);
}
使用连接句柄在内部调用
sqlite3_close_v2
会强制关闭连接并释放文件锁。