web-dev-qa-db-ja.com

SQLiteは、接続が閉じられた後でもデータベースをロックしたままにします

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

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

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

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

ASP.NETに固有の何かがあるかもしれません(マルチスレッドが問題ですか?開発マシンでテストしていますが、その関数への呼び出しは1つだけで、並行性はありませんか?)

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

36

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

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

あなたの質問を読んだ後、SQLiteCommandオブジェクトのみを使用して問題を再現しようとしましたが、それらを破棄しないと問題が発生することがわかりました。 2012-11-27 19:37 UTC更新:これは、System.Data.SQLiteの this ticket によってさらに確認されます。開発者は、「all接続に関連付けられた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's answer は2つの重要なことを強調しています:

  • system.Data.SQLiteの公式サイトの changelog によると、バージョン1.0.84.0以降では、ライブラリがこれを処理するため、上記のコードは必要ありません。私はこれをテストしていませんが、最悪の場合、このスニペットのみが必要です:

    //In Table Adapter    
    protected override void Dispose(bool disposing)
    {
      base.Dispose(disposing);
    
      this.Adapter.Dispose();
    }
    
  • DisposeTableAdapter呼び出しの実装について:データセットの再生成がこのコード(および追加可能なコードに影響を与えないように、これを部分クラスに配置することをお勧めします追加する必要があります)。

36
edymtt

私は同じ問題を抱えています。私のシナリオは、SQLiteデータベースファイル内のデータを取得した後、そのファイルを削除したいが、常にエラーをスローする "... using by other process "。 SqliteConnectionまたはSqliteCommandを破棄しても、エラーが発生します。 GC.Collect()を呼び出してエラーを修正しました。

コードスニペット

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

    GC.Collect();
}

この助けを願っています。

25
klaydze

私の場合、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);
9
Nate

次は私のために働いた:MySQLiteConnection.Close(); SQLite.SQLiteConnection.ClearAllPools()

6
Arvin

ほとんどの場合、リーダーとコマンドを適切に配置しないと問題が発生します。コマンドとリーダーが適切に破棄されないシナリオがあります。

シナリオ1:boolean関数を実行している場合。結果に到達する前に、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;
    }

Solution:次のように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();
        }
2
desw

TableAdapters/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());
}
1
gorogm

前述のように、SQLiteオブジェクトは破棄する必要があります。ただし、奇妙な動作があります。コマンドの呼び出し中に接続を開く必要があります。例えば:

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

同じファイルロックを与える

1

IDisposable(SQLiteConnection、SQLiteCommandなど)を適切に破棄することで、この問題を解決できます。私は、使い捨ての資源を適切に廃棄するために、習慣として「使用」を使用しなければならないことを繰り返し述べなければなりません。

0
pengu1n

これは、このエラーに遭遇したときに見つけた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
0
relic120

コンピューターをロックすると、ロックを解除した後でも問題はありませんでしたが、幸運なことに、最近ロックを開始し、数日前にソフトウェアがリリースされました。

とにかく、接続を閉じる、ClearAllPoolsなどのすべてのものがありましたが、aTableAdapter.Adapter.Dispose()が欠落していたので修正しました。

0
colin lamarre

私は同じ問題を抱えていましたが、DbCommandステートメントにusingを配置することで修正されましたが、Pooling = true私の問題は修正されました!!

                SQLiteConnectionStringBuilder builder = new SQLiteConnectionStringBuilder
                {
                    Pooling = true
                };
0
Dominic Jonas