40

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

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

我的代码非常简单,我打开连接,从 SQLServer 数据库中读取一些数据,将这些数据插入 SQLite(通过 SQLiteDataAdapter),然后关闭连接并处理所有内容以确保安全。但是,当我在填充数据后尝试压缩文件时,仍然会出现该错误。

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

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

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

4

12 回答 12

36

使用 1.0.82.0 版附带的设计器生成的数据集/表适配器时,我遇到了同样的问题System.Data.Sqlite.dll——关闭连接后,我们无法使用System.IO.FileStream. 我正确配置了连接和表格适配器,并且没有使用连接池。

根据我的第一次搜索(例如thisthis thread),库本身似乎存在问题——对象未正确释放和/或池问题(我不使用)。

在阅读了您的问题后,我尝试仅使用 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();
            }
        }
    }
}

更新 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();
    }
    
  • 关于Dispose调用的实现TableAdapter:最好将其放在部分类中,以便数据集重新生成不会影响此代码(以及您可能需要添加的任何其他代码)。

于 2012-10-01T19:36:16.533 回答
28

我也有同样的问题。我的场景是在获取 SQLite 数据库文件中的数据后,我想删除该文件,但它总是抛出一个错误“ ......由其他进程使用”。即使我处理了 SqliteConnection 或 SqliteCommand 错误仍然发生。我已经通过调用修复了错误GC.Collect()

代码片段

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

    GC.Collect();
}

希望这有帮助。

于 2013-01-04T06:02:29.790 回答
10

就我而言,我是在创建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);
于 2013-04-04T20:23:54.017 回答
9

以下对我有用:

MySQLiteConnection.Close();
SQLite.SQLiteConnection.ClearAllPools()
于 2014-07-04T08:56:21.943 回答
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();
        }
于 2014-01-28T15:00:54.770 回答
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());
}
于 2013-07-04T18:28:59.117 回答
1

如前所述,必须销毁 SQLite 对象。但是,有一个奇怪的行为:在调用 Dispose 命令期间必须打开连接。例如:

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

给出相同的文件锁

于 2015-10-07T20:03:49.153 回答
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
于 2014-04-24T16:15:52.777 回答
0

确保正确处理任何 IDisposable(例如 SQLiteConnection、SQLiteCommand 等)可以解决此问题。我应该重申,必须将“使用”作为一种习惯,以确保妥善处理一次性资源。

于 2015-07-18T04:39:17.570 回答
0

我遇到了同样的问题,只能通过DbCommandusing语句中处理来解决,但Pooling = true我的问题已经解决了!!

                SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder
                {
                    Pooling = true
                };
于 2016-11-11T08:49:05.193 回答
0

我只是在锁定计算机时遇到了这里提到的问题,即使在解锁它之后,它也可以正常工作,否则很幸运,因为我最近才开始锁定它,而且该软件几天前才发布,还没有人知道它。

无论如何,我有所有的东西,比如关闭连接和 ClearAllPools 等,但是缺少 aTableAdapter.Adapter.Dispose() 并修复了它。

于 2018-06-12T03:54:20.973 回答
0

有同样的问题。只需安装 SQLite 1.0.111(通过 nuget)即可解决。

不必更改我的代码上的任何内容,只需更新到该版本即可。以前我使用 1.0.85 文件被锁定的地方,尽管连接被关闭和处理。

于 2019-09-20T05:32:45.060 回答