SQLite DB 文件句柄永远不会关闭 C#

问题描述 投票:0回答:2

我正在尝试从我的 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
    范围
  • 杀死所有矿池
  • 执行GC并等待其完成

我确实需要从代码内部删除我的数据库文件,但我不能,因为文件句柄从未关闭。

我该如何解决这个问题?

编辑1:

  • 使用 .NET Winforms 应用程序与
    System.Data.SQLite
  • 已尝试将
    Pooling=false
    添加到连接字符串中。
c# sqlite file delete-file handle
2个回答
1
投票

您是否也尝试过关闭 SQLiteCommand?

using (SQLiteConnection connection = new SQLiteConnection(connString)) {
    using (SQLiteCommand cmd = connection.CreateCommand()){
        // do stuff here
    }
}
SQLiteConnection.ClearAllPools();
GC.Collect();
GC.WaitForPendingFinalizers();

0
投票

以下是我如何使用 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
会强制关闭连接并释放文件锁。

© www.soinside.com 2019 - 2024. All rights reserved.