40

ASP.NET アプリケーション (フレームワーク 4.0) で System.Data.SQLite プロバイダーを使用しています。私が直面している問題は、SQLite データベースのテーブルに何かを INSERT すると、データベースがロックされ、接続が破棄された後でもロックが解放されないことです。

ファイルにアクセスしようとすると、「別のプロセスで使用されているため、プロセスはファイル 'catalog.sqlite' にアクセスできません。」というエラーが表示されます。

私のコードは非常に簡単です。接続を開き、SQLServer データベースからデータを読み取り、そのデータを (SQLiteDataAdapter を介して) SQLite に挿入し、接続を閉じて、安全のためにすべてを破棄します。それでも、データが取り込まれた後にファイルを圧縮しようとすると、そのエラーが発生します。

StackOverflow であらゆる種類の提案を読みましたが、問題の解決に役立ったものはありません (ウイルス対策をオフにする、トランザクション モデルを変更する、ファイルを圧縮する前に数秒待機する、すべての挿入呼び出しをトランザクションにラップする、など..しかし、この問題の解決に役立ったものはありません。

たぶん、ASP.NETに固有の何かがあります(マルチスレッドが問題ですか?その関数への呼び出しが1つしかなく、同時実行がない開発マシンでテストしていますが?)

補足として、私は DataTable と SQLiteDataAdapter を避け、SQLiteCommand のみを直接使用しようとしましたが、それは魅力的です。もちろん、データ アダプターを使用する代わりに、クエリを文字列として作成し続けることもできますが、それを行うために作成されたフレームワークがあると、少し厄介です。

4

12 に答える 12

36

バージョン 1.0.82.0に同梱されているデザイナーで生成されたデータセット/テーブルアダプターを使用して同じ問題が発生しましSystem.Data.Sqlite.dllた。接続を閉じた後、を使用してデータベース ファイルを読み取ることができませんでしたSystem.IO.FileStream。接続とテーブルアダプターの両方を正しく処分していましたが、接続プーリングを使用していませんでした。

私の最初の検索 (たとえば、 thisおよびthis thread ) によると、ライブラリ自体に問題があるように見えました。オブジェクトが正しくリリースされていないか、プールの問題 (私は使用していません) のいずれかです。

あなたの質問を読んだ後、私は SQLiteCommand オブジェクトのみを使用して問題を再現しようとしましたが、それらを破棄しないと問題が発生することがわかりました。Update 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 の回答では、2 つの重要な点が強調されています。

  • 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

TableAdapter / Datasetsのせいにすることについてのedymttの答えは正しいと思いましたが、毎回再生成された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 検索結果の 1 つです。ただし、どの応答も役に立たなかったので、さらに検索してグーグルで調べた後、 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

DbCommand私は同じ問題を抱えていて、それはusing声明で処分することによってのみ修正されましたが、Pooling = true私の問題は修正されました!!

                SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder
                {
                    Pooling = true
                };
于 2016-11-11T08:49:05.193 に答える