我在ASP.NET应用程序(框架4.0)中使用System.Data.SQLite提供程序。 我遇到的问题是,当我在SQLite数据库的表中插入某些内容时,数据库会被锁定,即使在连接处理后锁也没有被释放。
尝试访问该文件时,错误是:“进程无法访问文件'catalog.sqlite',因为它正由另一个进程使用。”
我的代码非常简单,我打开连接,从SQLServer数据库读取一些数据,将数据插入SQLite(通过SQLiteDataAdapter),然后关闭连接并处置所有内容以保证安全。但是,当我在填充数据后尝试压缩文件时,我得到了这个错误。
我已经在StackOverflow上阅读了所有类型的建议,但没有一个能帮助解决问题(关闭防病毒软件,更改事务模型,在压缩文件之前等待几秒钟,将所有插入调用包装到交易等等。但没有人帮助解决这个问题。
也许有一些特定于ASP.NET的东西(多线程是问题?即使我在开发机器上测试它,只有一次调用该函数而没有并发?)
作为旁注,我尝试避免使用DataTable和SQLiteDataAdapter,并且直接使用SQLiteCommand,这样就可以实现魅力。当然,我可以继续将查询构建为字符串而不是使用数据适配器,但是当我为此构建一个框架时,我发现它有点尴尬。
答案 0 :(得分:35)
使用随System.Data.Sqlite.dll
版本1.0.82.0附带的设计器生成的数据集/ tableadapter,我遇到了同样的问题 - 关闭连接后,我们无法使用System.IO.FileStream
读取数据库文件。我正确处理了连接和tableadapter,我没有使用连接池。
根据我的第一次搜索(例如this和this thread),这似乎是库本身的问题 - 对象未正确发布和/或汇集问题(我不使用)。
在阅读完问题之后,我尝试仅使用SQLiteCommand对象来复制问题,并且我发现当您不处理它们时会出现问题。 更新2012-11-27 19:37 UTC :this ticket对System.Data.SQLite进一步确认了这一点,其中开发人员解释说“ all 与连接关联的SQLiteCommand和SQLiteDataReader对象[应该]正确处理“。
然后我转回生成的TableAdapters,我发现没有Dispose
方法的实现 - 所以实际上创建的命令没有被处理掉。我实现了它,负责处理所有命令,我没有问题。
这是C#中的代码,希望这会有所帮助。请注意,代码是从original in Visual Basic转换而来的,因此需要一些转换错误。
//In Table Adapter
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
Common.DisposeTableAdapter(disposing, _adapter, _commandCollection);
}
public static class Common
{
/// <summary>
/// Disposes a TableAdapter generated by SQLite Designer
/// </summary>
/// <param name="disposing"></param>
/// <param name="adapter"></param>
/// <param name="commandCollection"></param>
/// <remarks>You must dispose all the command,
/// otherwise the file remains locked and cannot be accessed
/// (for example, for reading or deletion)</remarks>
public static void DisposeTableAdapter(
bool disposing,
System.Data.SQLite.SQLiteDataAdapter adapter,
IEnumerable<System.Data.SQLite.SQLiteCommand> commandCollection)
{
if (disposing) {
DisposeSQLiteTableAdapter(adapter);
foreach (object currentCommand_loopVariable in commandCollection)
{
currentCommand = currentCommand_loopVariable;
currentCommand.Dispose();
}
}
}
public static void DisposeSQLiteTableAdapter(
System.Data.SQLite.SQLiteDataAdapter adapter)
{
if (adapter != null) {
DisposeSQLiteTableAdapterCommands(adapter);
adapter.Dispose();
}
}
public static void DisposeSQLiteTableAdapterCommands(
System.Data.SQLite.SQLiteDataAdapter adapter)
{
foreach (object currentCommand_loopVariable in {
adapter.UpdateCommand,
adapter.InsertCommand,
adapter.DeleteCommand,
adapter.SelectCommand})
{
currentCommand = currentCommand_loopVariable;
if (currentCommand != null) {
currentCommand.Dispose();
}
}
}
}
更新2013-07-05 17:36 UTC gorogm's answer突出了两件重要的事情:
根据System.Data.SQLite官方网站上的changelog,从版本1.0.84.0开始,不需要上面的代码,因为库负责处理这个问题。我没有测试过这个,但在最坏的情况下你只需要这个片段:
//In Table Adapter
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
this.Adapter.Dispose();
}
关于Dispose
的{{1}}调用的实现:最好将它放在一个部分类中,以便数据集重新生成不会影响此代码(以及任何您可能需要添加的其他代码)。
答案 1 :(得分:24)
我有同样的问题。我的方案是在获取SQLite数据库文件中的数据之后我想要删除该文件,但它总是引发错误“ ......使用其他进程 ”。即使我处理SqliteConnection或SqliteCommand仍然会发生错误。我通过调用GC.Collect()
修正了错误。
代码段
public void DisposeSQLite()
{
SQLiteConnection.Dispose();
SQLiteCommand.Dispose();
GC.Collect();
}
希望得到这个帮助。
答案 2 :(得分:9)
就我而言,我正在创建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)
{
// Added using
using (var command = connection.CreateCommand())
{
command.CommandText = commandText;
return command.ExecuteScalar();
}
}
}
然后你可以像这样使用它
connection.ExecuteScalar(commandText);
答案 3 :(得分:6)
以下对我有用:
MySQLiteConnection.Close();
SQLite.SQLiteConnection.ClearAllPools()
答案 4 :(得分:2)
在大多数情况下,如果您没有妥善处理读者和命令,就会出现问题。在某种情况下,命令和读者将无法妥善处理。
场景1:如果您正在运行布尔函数。在到达结果之前,finally块中的代码不会超出。如果要在执行代码时评估函数isDataExists的结果,如果它适合结果,那么这是一个大问题,即
if(isDataExists){
// execute some code
}
正在评估的功能
public bool isDataExists(string sql)
{
try
{
OpenConnection();
SQLiteCommand cmd = new SQLiteCommand(sql, connection);
reader = cmd.ExecuteReader();
if (reader != null && reader.Read())
{
return true;
}
else
{
return false;
}
}
catch (Exception expMsg)
{
//Exception
}
finally
{
if (reader != null)
{
reader.Dispose();
}
CloseConnection();
}
return true;
}
解决方案:将您的阅读器和命令放在try块中,如下所示
OpenConnection();
SQLiteCommand cmd = new SQLiteCommand(sql, connection);
reader = cmd.ExecuteReader();
if (reader != null && reader.Read())
{
cmd.Dispose();
CloseConnection();
return true;
}
else
{
cmd.Dispose();
CloseConnection();
return false;
}
最后处理读者和命令以防万一出错
finally
{
if (reader != null)
{
reader.Dispose();
}
CloseConnection();
}
答案 5 :(得分:1)
我发现edymtt的答案是关于责备TableAdapters / Datasets,但是我没有修改每次重新生成的TableAdapter代码文件,而是找到了另一个解决方案:在TableAdapter的子代上手动调用.Dispose元素。 (在.NET 4.5中,最新的SQLite 1.0.86)
using (var db = new testDataSet())
{
using (testDataSetTableAdapters.UsersTableAdapter t = new testDataSetTableAdapters.UsersTableAdapter())
{
t.Fill(db.Users);
//One of the following two is enough
t.Connection.Dispose(); //THIS OR
t.Adapter.Dispose(); //THIS LINE MAKES THE DB FREE
}
Console.WriteLine((from x in db.Users select x.Username).Count());
}
答案 6 :(得分:1)
如前所述,必须销毁SQLite对象。但是,有一种奇怪的行为:在调用Dispose on命令期间必须打开连接。例如:
using(var connection = new SqliteConnection("source.db"))
{
connection.Open();
using(var command = connection.CreateCommand("select..."))
{
command.Execute...
}
}
工作正常,但是:
using(var connection = new SqliteConnection("source.db"))
{
connection.Open();
using(var command = connection.CreateCommand("select..."))
{
command.Execute...
connection.Close();
}
}
给出相同的文件锁
答案 7 :(得分:0)
这是我在遇到此错误时找到的google搜索结果之一。但是,没有一个回复帮助了我,所以经过更多的搜索和谷歌搜索后,我想出了这个代码来自http://www.tsjensen.com/blog/post/2012/11/10/SQLite-on-Visual-Studio-with-NuGet-and-Easy-Instructions.aspx的一些代码
但是,我根本不必使用NuGet。我的程序所做的是每次打开时从服务器下载一个db文件。然后,如果用户更新该数据库,它将被上传给每个人,以便下次他们打开相同的程序。在更新本地文件并尝试将其上载到SharePoint后,我收到了文件正在使用的错误。现在它工作正常。
Public Function sqLiteGetDataTable(sql As String) As DataTable
Dim dt As New DataTable()
Using cnn = New SQLiteConnection(dbConnection)
cnn.Open()
Using cmd As SQLiteCommand = cnn.CreateCommand()
cmd.CommandText = sql
Using reader As System.Data.SQLite.SQLiteDataReader = cmd.ExecuteReader()
dt.Load(reader)
reader.Dispose()
End Using
cmd.Dispose()
End Using
If cnn.State <> System.Data.ConnectionState.Closed Then
cnn.Close()
End If
cnn.Dispose()
End Using
Return dt
End Function
答案 8 :(得分:0)
确保正确处理任何IDisposable(例如,SQLiteConnection,SQLiteCommand等)可以解决此问题。我应该重申,必须使用“使用”作为习惯,以确保妥善处置一次性资源。
答案 9 :(得分:0)
我遇到了同样的问题而且只是通过在Locale locale = new Locale("zh","TW");
声明中处理DbCommand
来解决问题,但是using
我的问题已得到修复!!
Pooling = true
答案 10 :(得分:0)
当我锁定计算机时,我只是遇到了这里提到的问题,即使在解锁之后,工作正常,否则很幸运,因为我刚刚开始锁定它并且软件刚刚在几天前发布,然后才有人知道它。
无论如何,我有所有的东西,如关闭连接和ClearAllPools等,但缺少aTableAdapter.Adapter.Dispose()并修复它。
答案 11 :(得分:0)
有同样的问题。 只需安装SQLite 1.0.111(通过nuget)即可解决。
不必更改我的代码,只需更新到该版本即可。以前,我使用1.0.85锁定了文件,尽管连接已关闭并被清除。