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

在试图删除文件之前,我的数据库关闭出现了问题

 myconnection.Close();
File.Delete(filename);

Delete 引发文件仍在使用的异常。几分钟后,我在调试器中重试了 Delete () ,所以这不是计时问题。

I have transaction code but it doesn't run at all before the Close() call. So I'm fairly sure it's not an open transaction. The sql commands between open and close are just selects.

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

Visual Studio 2010,C # ,System. Data.SQLite version 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);
59359 次浏览

不久前,我在为 C # 编写数据库抽象层时遇到了同样的问题,我一直没有找到问题所在。当您试图使用我的库删除 SQLiteDB 时,我刚刚抛出了一个异常。

不管怎样,今天下午我又仔细查看了一遍我觉得我应该试着找出为什么它会一劳永逸地这么做所以这是我目前为止的发现。

What happens when you call SQLiteConnection.Close() is that (along with a number of checks and other things) the SQLiteConnectionHandle that points to the SQLite database instance is disposed. This is done through a call to SQLiteConnectionHandle.Dispose(), however this doesn't actually release the pointer until the CLR's Garbage Collector performs some garbage collection. Since SQLiteConnectionHandle overrides the CriticalHandle.ReleaseHandle() function to call sqlite3_close_interop() (through another function) this does not close the database.

从我的角度来看,这是一个非常糟糕的做事方式,因为程序员实际上并不确定什么时候数据库被关闭,但这是它已经做到的方式,所以我想我们必须生活在它现在,或提交一些更改系统。Data.SQLite.欢迎任何志愿者这样做,不幸的是,我没有时间这样做明年之前。

DR 解决方案是在调用 SQLiteConnection.Close()之后和调用 File.Delete()之前强制 GC。

下面是示例代码:

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

祝你好运,希望能有所帮助

我有一个类似的问题,我已经尝试与 GC.Collect的解决方案,但是,正如注意到,它可能需要很长的时间才能成为不锁定的文件。

我已经找到了另一种解决方案,它涉及到在 TableAdapter 中处理底层 SQLiteCommand,有关更多信息,请参见 这个答案

在我的例子中,我创建的是 SQLiteCommand对象,而没有显式地处置它们。

var command = connection.CreateCommand();
command.CommandText = commandText;
value = command.ExecuteScalar();

我用 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();
}
}
}

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

那么执行命令也会容易得多。

value = connection.ExecuteScalar(commandText)
// Command object created and disposed

Just GC.Collect() didn't work for me.

I had to add GC.WaitForPendingFinalizers() after GC.Collect() in order to proceed with the file deletion.

以下几点对我很有效:

MySQLiteConnection.Close();
SQLite.SQLiteConnection.ClearAllPools()

更多信息 : Connections are pooled by SQLite in order to improve performance.It means when you call Close method on a connection object, connection to database may still be alive (in the background) so that next Open method become faster.When you known that you don't want a new connection anymore, calling ClearAllPools closes all the connections which are alive in the background and file handle(s?) to the db file get released.Then db file may get removed, deleted or used by another process.

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

Found disposing of SQLiteCommand and SQLiteDataReader objects after use saved me using the garbage collector at all.

SQLiteCommand command = new SQLiteCommand(sql, db);
command.ExecuteNonQuery();
command.Dispose();

我相信打电话给 SQLite.SQLiteConnection.ClearAllPools()是最干净的解决方案。据我所知,在 WPF 环境中手动调用 GC.Collect()是不合适的。不过,在2016年3月升级到 System.Data.SQLite1.0.99.0之前,我并没有注意到这个问题

我在 EF 和 System.Data.Sqlite上遇到了同样的问题。

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

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

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

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);
}
}
}
}
}

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

我使用的是带有 EF6的 SQLite 1.0.101.0,并且在所有连接和实体被释放后,文件被锁定时遇到了麻烦。

这变得更糟糕,从 EF 的更新保持数据库锁定后,他们已经完成。 Collect ()是唯一有帮助的解决办法,我开始绝望了。

在绝望中,我尝试了 Oliver Wickenden 的 ClearSQLiteCommandConnectionHelper (参见他7月8日的回答)! 谢谢 Oliver。

我也遇到过类似的问题。真可惜... 我终于意识到 读者还没关门。出于某种原因,我认为当相应的连接关闭时,Reader 将被关闭。显然,GC.Collect ()不适合我。
用“ using: statement”包装 Reader 也是一个好主意。

static void Main(string[] args)
{
try
{
var dbPath = "myTestDb.db";
ExecuteTestCommand(dbPath);
File.Delete(dbPath);
Console.WriteLine("DB removed");
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
Console.Read();
}


private static void ExecuteTestCommand(string dbPath)
{
using (var connection = new SQLiteConnection("Data Source=" + dbPath + ";"))
{
using (var command = connection.CreateCommand())
{
command.CommandText = "PRAGMA integrity_check";
connection.Open();
var reader = command.ExecuteReader();
if (reader.Read())
Console.WriteLine(reader.GetString(0));


//without next line database file will remain locked
reader.Close();
}
}
}

试试这个... 这个尝试所有以上的 密码... 为我工作

    Reader.Close()
connection.Close()
GC.Collect()
GC.WaitForPendingFinalizers()
command.Dispose()
SQLite.SQLiteConnection.ClearAllPools()

希望能帮上忙

也许您根本不需要处理 GC。请检查是否所有 sqlite3_prepare都已完成。

对于每个 sqlite3_prepare,您需要一个对应的 sqlite3_finalize

如果没有正确地完成,sqlite3_close将不会关闭连接。

使用 GC.WaitForPendingFinalizers()

例如:

Con.Close();
GC.Collect();`
GC.WaitForPendingFinalizers();
File.Delete(Environment.CurrentDirectory + "\\DATABASENAME.DB");

Had a similar problem. Calling Garbage Collector didn't help me. LAter I found a way to solve the problem

作者还写道,在试图删除该数据库之前,他对该数据库进行了 SELECT 查询。我也有同样的情况。

我有以下密码:

SQLiteConnection bc;
string sql;
var cmd = new SQLiteCommand(sql, bc);
SQLiteDataReader reader = cmd.ExecuteReader();
reader.Read();
reader.Close(); // when I added that string, the problem became solved.

另外,我不需要关闭数据库连接并调用垃圾收集器。我所要做的就是关闭在执行 SELECT 查询时创建的读取器

Waiting for Garbage Collector may not release the database all time and that happened to me. When some type of Exception occurs in SQLite database for example trying to insert a row with existing value for PrimaryKey it will hold the database file until you dispose it. Following code catches SQLite exception and cancels problematic command.

SQLiteCommand insertCommand = connection.CreateCommand();
try {
// some insert parameters
insertCommand.ExecuteNonQuery();
} catch (SQLiteException exception) {
insertCommand.Cancel();
insertCommand.Dispose();
}

如果您不处理有问题的命令异常,那么垃圾收集器就无法处理这些异常,因为这些命令存在一些未处理的异常,所以它们不是垃圾。这种处理方法在等待垃圾收集器时很有效。

This works for me but i noticed sometimes journal files -wal -shm are not deleted when the process is closed. If you want SQLite to remove -wal -shm files when all connection are close the last connection closed MUST BE non-readonly. Hope this will help someone.

对我有效的最佳回答。

dbConnection.Close();
System.Data.SQLite.SQLiteConnection.ClearAllPools();


GC.Collect();
GC.WaitForPendingFinalizers();


File.Delete(Environment.CurrentDirectory + "\\DATABASENAME.DB");

其原因似乎是一个称为“池”的特性。 在连接字符串后面追加“ Poool = false”将导致使用“ Connection. Close ()”释放 DB-File。

在这里查看关于连接池的 FAQ: https://www.devart.com/dotconnect/sqlite/docs/FAQ.html#q54