即使在连接关闭后,SQLite也会使数据库保持锁定状态

Val*_*lli 36 c# sqlite system.data.sqlite

我在ASP.NET应用程序(框架4.0)中使用System.Data.SQLite提供程序.我遇到的问题是,当我在SQLite数据库的表中插入某些内容时,数据库会被锁定,即使在连接处理后锁也没有被释放.

尝试访问该文件时,错误是:"进程无法访问文件'catalog.sqlite',因为它正由另一个进程使用."

我的代码非常简单,我打开连接,从SQLServer数据库读取一些数据,将数据插入SQLite(通过SQLiteDataAdapter),然后关闭连接并处置所有内容以保证安全.但是,当我在填充数据后尝试压缩文件时,我得到了这个错误.

我已经在StackOverflow上阅读了所有类型的建议,但没有一个能帮助解决问题(关闭防病毒软件,更改事务模型,在压缩文件之前等待几秒钟,将所有插入调用包装到事务中,等等.但没有人帮助解决这个问题.

也许有一些特定于ASP.NET的东西(多线程是问题?即使我在开发机器上测试它,只有一个调用该函数而没有并发?)

作为旁注,我尝试避免使用DataTable和SQLiteDataAdapter,并且直接使用SQLiteCommand,这样就可以实现魅力.当然,我可以继续将查询构建为字符串而不是使用数据适配器,但是当有一个构建框架时,我发现它有点尴尬.

edy*_*mtt 36

我使用随1.0.82.0 System.Data.Sqlite.dll版附带的设计器生成的数据集/ tableadapter遇到了同样的问题- 在关闭连接后,我们无法使用读取数据库文件System.IO.FileStream.我正确处理连接和tableadapters,我没有使用连接池.

根据我的第一次搜索(例如这个这个线程),这似乎是库本身的问题 - 对象未正确发布和/或汇集问题(我不使用).

在阅读了你的问题之后,我试图仅使用SQLiteCommand对象来复制问题,并且我发现当你不处理它们时会出现问题.更新2012-11-27 19:37 UTC:System.Data.SQLite 的此票证进一步确认了这一点,其中开发人员解释说" 所有与连接相关的SQLiteCommand和SQLiteDataReader对象[应该]正确处理".

然后我转回生成的TableAdapters,我发现没有该Dispose方法的实现- 所以实际上创建的命令没有被处理掉.我实现了它,负责处理所有命令,我没有遇到任何问题.

这是C#中的代码,希望这会有所帮助.请注意,代码是从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();
            }
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

更新2013-07-05 17:36 UTC gorogm的回答强调了两件重要的事情:

  • 根据System.Data.SQLite官方网站上的更改日志,从版本1.0.84.0开始,不需要上面的代码,因为库负责处理这个问题.我没有测试过这个,但在最坏的情况下你只需要这个片段:

    //In Table Adapter    
    protected override void Dispose(bool disposing)
    {
      base.Dispose(disposing);
    
      this.Adapter.Dispose();
    }
    
    Run Code Online (Sandbox Code Playgroud)
  • 关于Dispose调用的实现TableAdapter:最好将它放在一个部分类中,这样数据集的重新生成不会影响这个代码(以及你可能需要添加的任何其他代码).


kla*_*dze 26

我也有同样的问题.我的方案是在获取SQLite数据库文件中的数据之后我想删除该文件,但它总是抛出错误" ......使用其他进程 ".即使我处理SqliteConnection或SqliteCommand仍然会发生错误.我通过调用修复了错误GC.Collect().

代码段

public void DisposeSQLite()
{
    SQLiteConnection.Dispose();
    SQLiteCommand.Dispose();

    GC.Collect();
}
Run Code Online (Sandbox Code Playgroud)

希望这有帮助.

  • @klaydze @Assassinbeast处理你提到的问题的一个更简洁的方法是调用`System.Data.SQLite.SQLiteConnection.ClearAllPools();`而不是两个dispose调用+ GC.Collect (4认同)
  • 在某些情况下,需要等待 GC 任务完成以使用带有 `GC.WaitForPendingFinalizers()` 的 sqlite 文件 (2认同)

Nat*_*ate 10

在我的情况下,我在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)
    {
        // Added using
        using (var command = connection.CreateCommand())
        {
            command.CommandText = commandText;
            return command.ExecuteScalar();
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

然后你可以像这样使用它

connection.ExecuteScalar(commandText);
Run Code Online (Sandbox Code Playgroud)

  • 我强烈建议不要吞咽这样的例外! (2认同)

Arv*_*vin 6

以下对我有用: MySQLiteConnection.Close(); SQLite.SQLiteConnection.ClearAllPools()