在尝试删除文件之前,我在关闭数据库时遇到问题。代码只是
myconnection.Close();
File.Delete(filename);
删除会抛出文件仍在使用中的异常。几分钟后我在调试器中重新尝试了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);
答案 0 :(得分:86)
在为C#编写数据库抽象层时,前一段时间遇到了同样的问题,我从未真正找到问题所在。当您尝试使用我的库删除SQLite数据库时,我刚刚抛出异常。
无论如何,今天下午我再次仔细研究了这一切,并想我会试着找出为什么一劳永逸地做到这一点,所以这就是我迄今为止所发现的。
当您调用SQLiteConnection.Close()
时会发生什么(以及一些检查和其他事情)指向SQLite数据库实例的SQLiteConnectionHandle
被释放。这是通过调用SQLiteConnectionHandle.Dispose()
来完成的,但是在CLR的垃圾收集器执行一些垃圾收集之前,这实际上不会释放指针。由于SQLiteConnectionHandle
会覆盖CriticalHandle.ReleaseHandle()
函数以调用sqlite3_close_interop()
(通过其他函数),因此不会关闭数据库。
从我的观点来看,这是一种非常糟糕的做事方式,因为程序员实际上并不确定数据库何时关闭,但这就是它已经完成的方式,所以我想我们现在必须忍受它,或者对System.Data.SQLite进行一些更改。欢迎任何志愿者这样做,不幸的是我在明年之前没有时间这样做。
<强> TL; 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);
祝你好运,我希望它有所帮助
答案 1 :(得分:54)
只是GC.Collect()
对我不起作用。
我必须在GC.WaitForPendingFinalizers()
之后添加GC.Collect()
才能继续删除文件。
答案 2 :(得分:15)
就我而言,我正在创建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
答案 3 :(得分:11)
有一个类似的问题,虽然垃圾收集器解决方案没有解决它。
在使用后发现处理SQLiteCommand
和SQLiteDataReader
对象完全使用垃圾收集器保存了我。
SQLiteCommand command = new SQLiteCommand(sql, db);
command.ExecuteNonQuery();
command.Dispose();
答案 4 :(得分:10)
以下对我有用:
MySQLiteConnection.Close();
SQLite.SQLiteConnection.ClearAllPools()
更多信息: 连接由SQLite汇集以提高性能。这意味着当您在连接对象上调用Close方法时,与数据库的连接可能仍然存在(在后台),以便下一个Open方法变得更快。当您知道您不知道时我想再建一个新的连接,调用ClearAllPools会关闭所有在后台处于活动状态的连接,并且db文件的文件句柄(s?)会被释放。然后另一个进程可能会删除,删除或使用db文件。
答案 5 :(得分:9)
我遇到了类似的问题,我已尝试使用GC.Collect
解决方案,但如上所述,文件未锁定可能需要很长时间。
我找到了一个替代解决方案,涉及处理TableAdapters中的基础SQLiteCommand
,有关其他信息,请参阅this answer。
答案 6 :(得分:3)
试试这个...这个尝试以上所有代码 ...为我工作
Reader.Close()
connection.Close()
GC.Collect()
GC.WaitForPendingFinalizers()
command.Dispose()
SQLite.SQLiteConnection.ClearAllPools()
希望有所帮助
答案 7 :(得分:3)
使用GC.WaitForPendingFinalizers()
示例:
Con.Close();
GC.Collect();`
GC.WaitForPendingFinalizers();
File.Delete(Environment.CurrentDirectory + "\\DATABASENAME.DB");
答案 8 :(得分:2)
我和EF System.Data.Sqlite
一直有同样的问题。
对我来说,我发现SQLiteConnection.ClearAllPools()
和GC.Collect()
会减少文件锁定发生的频率,但偶尔也会发生(大约1%的时间)。
我一直在调查,似乎EF创建的某些SQLiteCommand
没有被处理,并且仍然将其Connection属性设置为已关闭的连接。我尝试处理这些,但实体框架会在下一次DbContext
读取期间抛出异常 - 似乎EF在连接关闭后仍然会使用它们。
我的解决方案是确保在这些Null
上的连接关闭时,Connection属性设置为SQLiteCommand
。这似乎足以释放文件锁。我一直在测试下面的代码,经过几千次测试后没有看到任何文件锁定问题:
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();
。
然后,这将保留一个活动命令列表,并在指向已关闭的连接时将其连接设置为Null
。
答案 9 :(得分:2)
也许你根本不需要处理GC。请检查是否所有sqlite3_prepare
都已完成。
对于每个sqlite3_prepare
,您需要一位通讯员sqlite3_finalize
。
如果您没有正确完成,sqlite3_close
将不会关闭连接。
答案 10 :(得分:2)
有类似的问题。调用垃圾收集器并没有帮助我。我找到了解决问题的方法
作者还写道,在尝试删除之前,他对该数据库执行了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查询时创建的阅读器
答案 11 :(得分:1)
我相信对SQLite.SQLiteConnection.ClearAllPools()
的呼吁是最干净的解决方案。据我所知,在WPF环境中手动调用GC.Collect()
是不合适的。虽然,直到我在2016年3月升级到System.Data.SQLite
1.0.99.0
答案 12 :(得分:1)
我正在努力解决类似的问题。对我感到羞耻...我终于意识到读者没有关闭。出于某种原因,我认为当相应的连接关闭时,Reader将被关闭。显然,GC.Collect()并不适合我 使用&#34;使用:语句包装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();
}
}
}
答案 13 :(得分:0)
最佳答案。
public void cardClicked(final Sport currentSport){
itemCard.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(context, DetailActivity.class);
intent.putExtra("Image",R.drawable.img_baseball);
intent.putExtra("Name",currentSport.getTitle());
context.startActivity(intent);
}
});
}
答案 14 :(得分:0)
这对我有用,但是我注意到有时关闭进程时,日记文件-wal -shm不会被删除。如果您希望SQLite在所有连接均关闭时删除-wal -shm文件,则最后关闭的连接必须为非只读。希望这会帮助某人。
答案 15 :(得分:0)
等待垃圾收集器可能无法一直释放数据库并且发生在我身上。当在SQLite数据库中发生某种类型的异常时,例如尝试插入具有PrimaryKey现有值的行时,它将保留数据库文件,直到您将其处置为止。以下代码捕获SQLite异常并取消有问题的命令。
SQLiteCommand insertCommand = connection.CreateCommand();
try {
// some insert parameters
insertCommand.ExecuteNonQuery();
} catch (SQLiteException exception) {
insertCommand.Cancel();
insertCommand.Dispose();
}
如果你没有处理有问题的命令的异常,那么垃圾收集器就无法对它们做任何事情,因为这些命令有一些未处理的例外,因此它们不是垃圾。这种处理方法对我来说效果很好,等待垃圾收集器。
答案 16 :(得分:0)
我在使用带有EF6的SQLite 1.0.101.0时,在所有连接和实体都被处理后,文件被锁定时出现问题。
更新来自EF的更新使数据库在完成后保持锁定状态。 GC.Collect()是唯一有帮助的解决方法,我开始绝望了。
无奈之下,我尝试了Oliver Wickenden的ClearSQLiteCommandConnectionHelper(见他7月8日的回答)。太棒了。所有锁定问题都没了! 谢谢奥利弗。