System.Data.SQLite Close()不释放数据库文件

Tom*_*rul 82 sqlite system.data.sqlite

在尝试删除文件之前,我在关闭数据库时遇到问题.代码就是这样

 myconnection.Close();    
 File.Delete(filename);
Run Code Online (Sandbox Code Playgroud)

并且Delete抛出了一个异常,即该文件仍在使用中.几分钟后我在调试器中重新尝试了Delete(),所以这不是时间问题.

我有事务代码,但在Close()调用之前它根本不运行.所以我很确定这不是一个开放的交易.打开和关闭之间的sql命令只是选择.

ProcMon显示我的程序和我的防病毒软件查看数据库文件.它没有显示我的程序在close()之后释放db文件.

Visual Studio 2010,C#,System.Data.SQLite版本1.0.77.0,Win7

我看到一个两岁的bug就像这样,但更新日志说它已修复.

还有什么我可以检查的吗?有没有办法获得任何打开的命令或事务的列表?


新的工作代码:

 db.Close();
 GC.Collect();   // yes, really release the db

 bool worked = false;
 int tries = 1;
 while ((tries < 4) && (!worked))
 {
    try
    {
       Thread.Sleep(tries * 100);
       File.Delete(filename);
       worked = true;
    }
    catch (IOException e)   // delete only throws this on locking
    {
       tries++;
    }
 }
 if (!worked)
    throw new IOException("Unable to close file" + filename);
Run Code Online (Sandbox Code Playgroud)

Ben*_*ell 94

前一段时间遇到同样的问题,同时为C#编写了一个DB抽象层,我从来没有真正找到问题所在.当您尝试使用我的库删除SQLite数据库时,我刚刚抛出异常.

无论如何,今天下午我再次仔细研究了这一切,并想到我会试着找出为什么一劳永逸地这样做,所以这就是我迄今为止所发现的.

当你调用时会发生什么SQLiteConnection.Close()(以及一些检查和其他事情)SQLiteConnectionHandle指向SQLite数据库实例的处理.这是通过调用来完成的SQLiteConnectionHandle.Dispose(),但是在CLR的垃圾收集器执行一些垃圾收集之前,这实际上并没有释放指针.由于SQLiteConnectionHandle覆盖了CriticalHandle.ReleaseHandle()要调用的函数sqlite3_close_interop()(通过另一个函数),因此不会关闭数据库.

从我的观点来看,这是一种非常糟糕的做事方式,因为程序员实际上并不确定数据库何时关闭,但这就是它已经完成的方式所以我想我们现在必须忍受它,或者提交System.Data.SQLite的一些更改.欢迎任何志愿者这样做,不幸的是我在明年之前没有时间这样做.

TL; DR 解决方案是在您致电SQLiteConnection.Close()之前和之后强制使用GC File.Delete().

以下是示例代码:

string filename = "testFile.db";
SQLiteConnection connection = new SQLiteConnection("Data Source=" + filename + ";Version=3;");
connection.Close();
GC.Collect();
GC.WaitForPendingFinalizers();
File.Delete(filename);
Run Code Online (Sandbox Code Playgroud)

祝它好运,我希望它有所帮助

  • 所以GC.Collect(); 有效,但System.Data.SQLite.SQLiteConnection.ClearAllPools(); 使用库的API处理问题. (10认同)
  • 做得好!马上解决了我的问题.在C#开发的11年中,我从来没有必要使用GC.Collect:现在这是我被迫这样做的第一个例子. (2认同)
  • 还要注意,如果你打开了一个阅读器,你需要关闭它,如果你在连接和所有命令调用上调用 Dispose,你也可以跳过 `GC.Collect()` 选项。 (2认同)

小智 55

只是GC.Collect()没有为我工作.

我必须添加GC.WaitForPendingFinalizers()以后GC.Collect()才能继续删除文件.

  • 这并不奇怪,`GC.Collect()`只是启动一个异步的垃圾收集,所以为了确保所有清理过,你必须明确地等待它. (5认同)
  • 我经历了同样的事情,不得不添加GC.WaitForPendingFinalizers().这是在1.0.103 (2认同)

Nat*_*ate 17

在我的情况下,我在SQLiteCommand没有明确处理它们的情况下创建对象.

var command = connection.CreateCommand();
command.CommandText = commandText;
value = command.ExecuteScalar();
Run Code Online (Sandbox Code Playgroud)

我把我的命令包装在一个using声明中,它解决了我的问题.

static public class SqliteExtensions
{
    public static object ExecuteScalar(this SQLiteConnection connection, string commandText)
    {
        using (var command = connection.CreateCommand())
        {
            command.CommandText = commandText;
            return command.ExecuteScalar();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

using语句确保即使发生异常也会调用Dispose.

然后执行命令也容易得多.

value = connection.ExecuteScalar(commandText)
// Command object created and disposed
Run Code Online (Sandbox Code Playgroud)

  • 我非常建议不要吞咽这样的例外情况 (5认同)

Arv*_*vin 11

以下对我有用:

MySQLiteConnection.Close();
SQLite.SQLiteConnection.ClearAllPools()
Run Code Online (Sandbox Code Playgroud)

更多信息:连接由SQLite汇集以提高性能.这意味着当您在连接对象上调用Close方法时,与数据库的连接可能仍然存在(在后台),以便下一个Open方法变得更快.当您知道您不再需要新连接,调用ClearAllPools会关闭后台中存活的所有连接,并且db文件的文件句柄(s?)将被释放.然后,另一个进程可能会删除,删除或使用db文件.


小智 11

有一个类似的问题,虽然垃圾收集器解决方案没有解决它.

在使用后发现处理SQLiteCommandSQLiteDataReader对象使用垃圾收集器保存我.

SQLiteCommand command = new SQLiteCommand(sql, db);
command.ExecuteNonQuery();
command.Dispose();
Run Code Online (Sandbox Code Playgroud)

  • 确切地。确保你处理了每一个 `SQLiteCommand`,即使你稍后回收一个 `SQLiteCommand` 变量。 (2认同)
  • 伟大的!你节省了我很多时间。当我将“command.Dispose();”添加到每个执行的“SQLiteCommand”时,它修复了错误。 (2认同)

edy*_*mtt 9

我遇到了类似的问题,我已经尝试了解决方案GC.Collect但是,如上所述,在文件未锁定之前可能需要很长时间.

我找到了一个替代解决方案,涉及SQLiteCommand在TableAdapters中处理底层s,请参阅此答案以获取更多信息.

  • 在 SQLiteCommand 上调用 Dispose 对我有用。顺便说一句 - 如果你正在调用 GC.Collect 你做错了什么。 (2认同)

Hal*_*upa 6

我在 EF 和System.Data.Sqlite.

对我来说,我发现SQLiteConnection.ClearAllPools()GC.Collect()会减少文件锁定发生的频率,但它仍然偶尔会发生(大约 1% 的时间)。

我一直在调查,似乎SQLiteCommandEF 创建的某些s 没有被处理,并且它们的 Connection 属性仍然设置为关闭的连接。我尝试处理这些但实体框架会在下一次DbContext读取期间抛出异常- 似乎 EF 有时在连接关闭后仍然使用它们。

我的解决方案是确保Null在这些SQLiteCommands上关闭连接时将Connection 属性设置为。这似乎足以释放文件锁。我一直在测试下面的代码,经过几千次测试后没有看到任何文件锁定问题:

public static class ClearSQLiteCommandConnectionHelper
{
    private static readonly List<SQLiteCommand> OpenCommands = new List<SQLiteCommand>();

    public static void Initialise()
    {
        SQLiteConnection.Changed += SqLiteConnectionOnChanged;
    }

    private static void SqLiteConnectionOnChanged(object sender, ConnectionEventArgs connectionEventArgs)
    {
        if (connectionEventArgs.EventType == SQLiteConnectionEventType.NewCommand && connectionEventArgs.Command is SQLiteCommand)
        {
            OpenCommands.Add((SQLiteCommand)connectionEventArgs.Command);
        }
        else if (connectionEventArgs.EventType == SQLiteConnectionEventType.DisposingCommand && connectionEventArgs.Command is SQLiteCommand)
        {
            OpenCommands.Remove((SQLiteCommand)connectionEventArgs.Command);
        }

        if (connectionEventArgs.EventType == SQLiteConnectionEventType.Closed)
        {
            var commands = OpenCommands.ToList();
            foreach (var cmd in commands)
            {
                if (cmd.Connection == null)
                {
                    OpenCommands.Remove(cmd);
                }
                else if (cmd.Connection.State == ConnectionState.Closed)
                {
                    cmd.Connection = null;
                    OpenCommands.Remove(cmd);
                }
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

使用只需ClearSQLiteCommandConnectionHelper.Initialise();在应用程序加载开始时调用。然后,这将保留活动命令的列表,并Null在它们指向已关闭的连接时将它们的 Connection 设置为。


小智 5

试试这个……这个尝试了上面所有的代码……对我有用

    Reader.Close()
    connection.Close()
    GC.Collect()
    GC.WaitForPendingFinalizers()
    command.Dispose()
    SQLite.SQLiteConnection.ClearAllPools()
Run Code Online (Sandbox Code Playgroud)

希望有帮助


小智 5

使用GC.WaitForPendingFinalizers()

例子:

Con.Close();  
GC.Collect();`
GC.WaitForPendingFinalizers();
File.Delete(Environment.CurrentDirectory + "\\DATABASENAME.DB");
Run Code Online (Sandbox Code Playgroud)