System.Data.SQLite Close()未释放数据库文件
我在尝试删除文件之前关闭数据库时遇到问题。代码只是System.Data.SQLite Close()未释放数据库文件,sqlite,system.data.sqlite,Sqlite,System.data.sqlite,我在尝试删除文件之前关闭数据库时遇到问题。代码只是 myconnection.Close(); File.Delete(filename); 并且Delete抛出一个异常,该文件仍在使用中。几分钟后,我在调试器中重新尝试了Delete(),因此这不是时间问题 我有事务代码,但在Close()调用之前它根本不运行。所以我相当肯定这不是一笔公开交易。打开和关闭之间的sql命令只是选择 ProcMon显示我的程序和我的防病毒程序,查看数据库文件。它不显示我的程序在关闭()后释放db文件
myconnection.Close();
File.Delete(filename);
并且Delete抛出一个异常,该文件仍在使用中。几分钟后,我在调试器中重新尝试了Delete(),因此这不是时间问题
我有事务代码,但在Close()调用之前它根本不运行。所以我相当肯定这不是一笔公开交易。打开和关闭之间的sql命令只是选择
ProcMon显示我的程序和我的防病毒程序,查看数据库文件。它不显示我的程序在关闭()后释放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);
db.Close();
GC.Collect();//是的,真的释放数据库
布尔工作=假;
int=1;
而((尝试<4)和(!工作))
{
尝试
{
线程。睡眠(尝试*100);
删除(文件名);
工作=真实;
}
catch(ioe异常)//delete仅在锁定时抛出此
{
尝试++;
}
}
如果(!工作)
抛出新IOException(“无法关闭文件”+文件名);
不久前在为C#编写DB抽象层时遇到了同样的问题,而我从来没有真正找到问题所在。当您试图使用我的库删除SQLite DB时,我抛出了一个异常
不管怎样,今天下午我又看了一遍,我想我会努力找出它为什么要这样做,这就是我迄今为止的发现
调用SQLiteConnection.Close()
时发生的情况是,指向SQLite数据库实例的SQLiteConnectionHandle
被释放(以及一些检查和其他事情)。这是通过调用SQLiteConnectionHandle.Dispose()
完成的,但是在CLR的垃圾收集器执行一些垃圾收集之前,这实际上不会释放指针。由于SQLiteConnectionHandle
重写CriticalHandle.ReleaseHandle()
函数调用sqlite3\u close\u interop()
(通过另一个函数),因此不会关闭数据库
从我的观点来看,这是一种非常糟糕的方法,因为程序员实际上不确定数据库何时关闭,但这就是它的方式,所以我想我们现在必须接受它,或者对System.Data.SQLite进行一些更改。欢迎任何志愿者参加,不幸的是,我没有时间在明年之前参加
TL;DR
解决方案是在调用SQLiteConnection.Close()
之后和调用File.Delete()之前强制执行GC
以下是示例代码:
祝你好运,我希望这会有所帮助。不久前在为C#编写DB抽象层时遇到了同样的问题,而我从未真正找到问题所在。当您试图使用我的库删除SQLite DB时,我抛出了一个异常
不管怎样,今天下午我又看了一遍,我想我会努力找出它为什么要这样做,这就是我迄今为止的发现
调用SQLiteConnection.Close()
时发生的情况是,指向SQLite数据库实例的SQLiteConnectionHandle
被释放(以及一些检查和其他事情)。这是通过调用SQLiteConnectionHandle.Dispose()
完成的,但是在CLR的垃圾收集器执行一些垃圾收集之前,这实际上不会释放指针。由于SQLiteConnectionHandle
重写CriticalHandle.ReleaseHandle()
函数调用sqlite3\u close\u interop()
(通过另一个函数),因此不会关闭数据库
从我的观点来看,这是一种非常糟糕的方法,因为程序员实际上不确定数据库何时关闭,但这就是它的方式,所以我想我们现在必须接受它,或者对System.Data.SQLite进行一些更改。欢迎任何志愿者参加,不幸的是,我没有时间在明年之前参加
TL;DR
解决方案是在调用SQLiteConnection.Close()
之后和调用File.Delete()之前强制执行GC
以下是示例代码:
祝您好运,我希望这会有所帮助。我遇到了类似的问题,我尝试了使用GC.Collect
的解决方案,但是,如前所述,文件未锁定可能需要很长时间
我找到了一个替代解决方案,该解决方案涉及在TableAdapters中处理底层的SQLiteCommand
s,请参阅以获取更多信息。我遇到了类似的问题,我尝试了使用GC.Collect
的解决方案,但如前所述,可能需要很长时间才能将文件锁定
我找到了一个替代解决方案,涉及在TableAdapters中处理底层的SQLiteCommand
s,请参阅以获取更多信息。在我的例子中,我创建SQLiteCommand
对象时没有显式地处理它们
var command = connection.CreateCommand();
command.CommandText = commandText;
value = command.ExecuteScalar();
我将命令包装在一个语句中,它解决了我的问题
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
在我的例子中,我创建SQLiteCommand
对象时没有显式地处理它们
var command = connection.CreateCommand();
command.CommandText = commandText;
value = command.ExecuteScalar();
我将命令包装在一个语句中,它解决了我的问题
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
语句确保即使在
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);
}
}
}
}
}
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()
Con.Close();
GC.Collect();`
GC.WaitForPendingFinalizers();
File.Delete(Environment.CurrentDirectory + "\\DATABASENAME.DB");
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.
SQLiteCommand insertCommand = connection.CreateCommand();
try {
// some insert parameters
insertCommand.ExecuteNonQuery();
} catch (SQLiteException exception) {
insertCommand.Cancel();
insertCommand.Dispose();
}
dbConnection.Close();
System.Data.SQLite.SQLiteConnection.ClearAllPools();
GC.Collect();
GC.WaitForPendingFinalizers();
File.Delete(Environment.CurrentDirectory + "\\DATABASENAME.DB");